private void EmitBinaryOperatorExpression(BoundBinaryOperator expression, bool used) { var operatorKind = expression.OperatorKind; if (operatorKind.EmitsAsCheckedInstruction()) { EmitBinaryOperator(expression); } else { // if operator does not have side-effects itself and is not short-circuiting // we can simply emit side-effects from the first operand and then from the second one if (!used && !operatorKind.IsLogical() && !OperatorHasSideEffects(operatorKind)) { EmitExpression(expression.Left, false); EmitExpression(expression.Right, false); return; } if (IsConditional(operatorKind)) { EmitBinaryCondOperator(expression, true); } else { EmitBinaryOperator(expression); } } EmitPopIfUnused(used); }
private BoundNode RewriteStringConcatenation(BoundBinaryOperator node) { // UNDONE: We need to make this more sophisticated. For example, we should // UNDONE: be rewriting (M() + "A") + ("B" + N()) as // UNDONE: String.Concat(M(), "AB", N()). // UNDONE: We have many overloads of String.Concat to choose from: that // UNDONE: take one, two, three, four strings, that take params arrays // UNDONE: in strings and objects, and so on. See the native compiler // UNDONE: string rewriter for details. // UNDONE: For now, just to get this going let's do it the easy way; // UNDONE: we'll just generate calls to String.Concat(string, string) // UNDONE: or String.Concat(object, object) as appropriate. Debug.Assert(node != null); Debug.Assert(node.ConstantValueOpt == null); SpecialMember member = (node.OperatorKind == BinaryOperatorKind.StringConcatenation) ? SpecialMember.System_String__ConcatStringString : SpecialMember.System_String__ConcatObjectObject; var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); // UNDONE: Handle the bizarre error case where we don't have the expected string concat methods. Debug.Assert(method != null); return Visit(BoundCall.SynthesizedCall(null, method, node.Left, node.Right)); }
private BoundNode RewriteDelegateOperation(BoundBinaryOperator node, SpecialMember member) { Debug.Assert(node != null); var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); // UNDONE: Handle the bizarre error case where we don't have the expected methods. Debug.Assert(method != null); BoundExpression call = BoundCall.SynthesizedCall(null, method, node.Left, node.Right); BoundExpression result = method.ReturnType.SpecialType == SpecialType.System_Delegate ? BoundConversion.SynthesizedConversion(call, ConversionKind.ExplicitReference, node.Type) : call; return Visit(result); }
private BoundNode RewriteStringEquality(BoundBinaryOperator node, SpecialMember member) { Debug.Assert(node != null); Debug.Assert(node.ConstantValueOpt == null); if (node.Left.ConstantValue == ConstantValue.Null || node.Right.ConstantValue == ConstantValue.Null) { return base.VisitBinaryOperator(node); } var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(member); Debug.Assert(method != null); return Visit(BoundCall.SynthesizedCall(null, method, node.Left, node.Right)); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { Debug.Assert(node != null); BoundExpression left = (BoundExpression)this.Visit(node.Left); BoundExpression right = (BoundExpression)this.Visit(node.Right); if (node.Type.SpecialType == SpecialType.System_Decimal) { return RewriteDecimalArithmeticBinaryOperator(node.OperatorKind, node.Syntax, left, right); } else if (node.Left.Type.SpecialType == SpecialType.System_Decimal && node.Left.Type.SpecialType == SpecialType.System_Decimal) { return RewriteDecimalComparisonOperator(node.OperatorKind, node.Syntax, left, right); } return node.Update(node.OperatorKind, left, right, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, node.Type); }
private void EmitShortCircuitingOperator( BoundBinaryOperator condition, bool sense, bool stopSense, bool stopValue ) { // we generate: // // gotoif (a == stopSense) fallThrough // b == sense // goto labEnd // fallThrough: // stopValue // labEnd: // AND OR // +- ------ ----- // stopSense | !sense sense // stopValue | 0 1 object lazyFallThrough = null; EmitCondBranch(condition.Left, ref lazyFallThrough, stopSense); EmitCondExpr(condition.Right, sense); // if fall-through was not initialized, no-one is going to take that branch // and we are done with Right on stack if (lazyFallThrough == null) { return; } var labEnd = new object(); _builder.EmitBranch(ILOpCode.Br, labEnd); // if we get to fallThrough, we should not have Right on stack. Adjust for that. _builder.AdjustStack(-1); _builder.MarkLabel(lazyFallThrough); _builder.EmitBoolConstant(stopValue); _builder.MarkLabel(labEnd); }
private void EmitBinaryCheckedOperatorInstruction(BoundBinaryOperator expression) { var unsigned = IsUnsignedBinaryOperator(expression); switch (expression.OperatorKind.Operator()) { case BinaryOperatorKind.Multiplication: if (unsigned) { _builder.EmitOpCode(ILOpCode.Mul_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Mul_ovf); } break; case BinaryOperatorKind.Addition: if (unsigned) { _builder.EmitOpCode(ILOpCode.Add_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Add_ovf); } break; case BinaryOperatorKind.Subtraction: if (unsigned) { _builder.EmitOpCode(ILOpCode.Sub_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Sub_ovf); } break; default: throw ExceptionUtilities.UnexpectedValue(expression.OperatorKind.Operator()); } }
protected override BoundStatement RewriteForStatement(BoundForStatement node) { // for <var> = <lower> to <upper> // <body> // // ----> // // { // var <var> = <lower> // while (<var> <= <upper>) // { // <body> // <var> = <var> + 1 // } // } var variableDeclaration = new BoundVeriableDeclaration(node.Variable, node.LowerBound); var variableExpression = new BoundVariableExpression(node.Variable); var upperBoundSybmle = new VariableSymble("upperBound", true, typeof(int)); var upperBoundDeclaration = new BoundVeriableDeclaration(upperBoundSybmle, node.UpperBound); var condition = new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.LessOrEqualToken, typeof(int), typeof(int)), new BoundVariableExpression(upperBoundSybmle) ); var increment = new BoundExpressionStatemnet( new BoundAssignmentExpression( node.Variable, new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.PlusToken, typeof(int), typeof(int)), new BoundLiteralExpression(1) ) ) ); var whileBody = new BoundBlockStatemnet(ImmutableArray.Create <BoundStatement>(node.Body, increment)); var whileStatement = new BoundWhileStatement(condition, whileBody); var result = new BoundBlockStatemnet(ImmutableArray.Create <BoundStatement>( variableDeclaration, upperBoundDeclaration, whileStatement)); return(RewriteStatement(result)); }
private BoundExpression BindBinaryExpression(BinaryExpressionSyntax syntax) { var boundLeft = this.BindExpression(syntax.Left); var boundRight = this.BindExpression(syntax.Right); if (boundLeft.Type == TypeSymbol.Error || boundRight.Type == TypeSymbol.Error) { return(new BoundErrorExpression(syntax)); } var boundOperator = BoundBinaryOperator.Bind(syntax.OperatorToken.Kind, boundLeft.Type, boundRight.Type); if (boundOperator == null) { this.Diagnostics.ReportUndefinedBinaryOperator(syntax.OperatorToken.Location, syntax.OperatorToken.Text, boundLeft.Type, boundRight.Type); return(new BoundErrorExpression(syntax)); } return(new BoundBinaryExpression(syntax, boundLeft, boundOperator, boundRight)); }
private static bool IsUnsignedBinaryOperator(BoundBinaryOperator op) { BinaryOperatorKind opKind = op.OperatorKind; BinaryOperatorKind type = opKind.OperandTypes(); switch (type) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: return(IsUnsigned( Binder.GetEnumPromotedType(op.Left.Type.GetEnumUnderlyingType().SpecialType) )); case BinaryOperatorKind.UnderlyingAndEnum: return(IsUnsigned( Binder.GetEnumPromotedType( op.Right.Type.GetEnumUnderlyingType().SpecialType ) )); case BinaryOperatorKind.UInt: case BinaryOperatorKind.NUInt: case BinaryOperatorKind.ULong: case BinaryOperatorKind.ULongAndPointer: case BinaryOperatorKind.PointerAndInt: case BinaryOperatorKind.PointerAndUInt: case BinaryOperatorKind.PointerAndLong: case BinaryOperatorKind.PointerAndULong: case BinaryOperatorKind.Pointer: return(true); // Dev10 bases signedness on the first operand (see ILGENREC::genOperatorExpr). case BinaryOperatorKind.IntAndPointer: case BinaryOperatorKind.LongAndPointer: // Dev10 converts the uint to a native int, so it counts as signed. case BinaryOperatorKind.UIntAndPointer: default: return(false); } }
protected override BoundStatement RewriteForStatement(BoundForStatement node) { // for <var> = <lower> to <upper> // <body> // is rewritten to // { // var <Var> = <lower> // while () // var variableDeclaration = new BoundVariableDeclaration(node.Variable, node.LowerBound); var variableExpression = new BoundVariableExpression(node.Variable); var condition = new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.LessOrEqualsToken, typeof(int), typeof(int)), node.UpperBound); var increment = new BoundExpressionStatement( new BoundAssignmentExpression( node.Variable, new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind( SyntaxKind.PlusToken, typeof(int), typeof(int)), new BoundLiteralExpression(1) ) ) ); var whileBlock = new BoundBlockStatement(ImmutableArray.Create <BoundStatement>(node.Body, increment)); var whileStatement = new BoundWhileStatement(condition, whileBlock); var result = new BoundBlockStatement(ImmutableArray.Create <BoundStatement>(variableDeclaration, whileStatement)); return(RewriteStatement(result)); }
protected override BoundStatement RewriteForStatement(BoundForStatement node) { BoundVariableDeclaration variableDeclaration = new BoundVariableDeclaration(node.Variable, node.LowerBound); BoundVariableExpression variableExpression = new BoundVariableExpression(node.Variable); VariableSymbol upperVariableSymbol = new LocalVariableSymbol("upperBound", true, TypeSymbol.Int); BoundVariableDeclaration upperVariableDeclaration = new BoundVariableDeclaration(upperVariableSymbol, node.UpperBound); BoundBinaryExpression condition = new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.LessOrEqualsToken, TypeSymbol.Int, TypeSymbol.Int), new BoundVariableExpression(upperVariableSymbol) ); BoundLabelStatement continueLabelStatement = new BoundLabelStatement(node.ContinueLabel); BoundExpressionStatement increment = new BoundExpressionStatement( new BoundAssignmentExpression( node.Variable, new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.PlusToken, TypeSymbol.Int, TypeSymbol.Int), new BoundLiteralExpression(1) ) ) ); BoundBlockStatement whileBody = new BoundBlockStatement(ImmutableArray.Create <BoundStatement>( node.Body, continueLabelStatement, increment )); BoundWhileStatement whileStatement = new BoundWhileStatement(condition, whileBody, node.BreakLabel, GenerateLabel()); BoundBlockStatement result = new BoundBlockStatement(ImmutableArray.Create <BoundStatement>( variableDeclaration, upperVariableDeclaration, whileStatement )); return(RewriteStatement(result)); }
public static BoundConstant?FoldBinary(BoundBinaryOperator op, BoundExpression left, BoundExpression right) { if (op == BoundBinaryOperator.LogicalAnd) { if (!(left.Constant is null) && left.Constant.Value is bool b1 && !b1 || !(right.Constant is null) && right.Constant.Value is bool b2 && !b2) { return(new BoundConstant(false)); } } if (op == BoundBinaryOperator.LogicalAnd) { if (!(left.Constant is null) && left.Constant.Value is bool b1 && b1 || !(right.Constant is null) && right.Constant.Value is bool b2 && b2) { return(new BoundConstant(true)); } } if (left.Constant is null || right.Constant is null) { return(null); } var leftVal = left.Constant.Value; var rightVal = right.Constant.Value; object res; switch (op) { case BoundBinaryOperator.Addition: { if (leftVal is int i1 && rightVal is int i2) { res = i1 + i2; }
//NOTE: The result of this should be a boolean on the stack. private void EmitBinaryCondOperator(BoundBinaryOperator binOp, bool sense) { bool andOrSense = sense; int opIdx; switch (binOp.OperatorKind.OperatorWithLogical()) { case BinaryOperatorKind.LogicalOr: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // Rewrite (a || b) as ~(~a && ~b) andOrSense = !andOrSense; // Fall through goto case BinaryOperatorKind.LogicalAnd; case BinaryOperatorKind.LogicalAnd: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // ~(a && b) is equivalent to (~a || ~b) if (!andOrSense) { // generate (~a || ~b) EmitShortCircuitingOperator(binOp, sense, sense, true); } else { // generate (a && b) EmitShortCircuitingOperator(binOp, sense, !sense, false); } return; case BinaryOperatorKind.And: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); EmitBinaryCondOperatorHelper(ILOpCode.And, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.Or: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); EmitBinaryCondOperatorHelper(ILOpCode.Or, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.Xor: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // Xor is equivalent to not equal. if (sense) EmitBinaryCondOperatorHelper(ILOpCode.Xor, binOp.Left, binOp.Right, true); else EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, true); return; case BinaryOperatorKind.NotEqual: // neq is emitted as !eq sense = !sense; goto case BinaryOperatorKind.Equal; case BinaryOperatorKind.Equal: var constant = binOp.Left.ConstantValue; var comparand = binOp.Right; if (constant == null) { constant = comparand.ConstantValue; comparand = binOp.Left; } if (constant != null) { if (constant.IsDefaultValue) { if (!constant.IsFloating) { if (sense) { EmitIsNullOrZero(comparand, constant); } else { // obj != null/0 for pointers and integral numerics is emitted as cgt.un EmitIsNotNullOrZero(comparand, constant); } return; } } else if (constant.IsBoolean) { // treat "x = True" ==> "x" EmitExpression(comparand, true); EmitIsSense(sense); return; } } EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.LessThan: opIdx = 0; break; case BinaryOperatorKind.LessThanOrEqual: opIdx = 1; sense = !sense; // lte is emitted as !gt break; case BinaryOperatorKind.GreaterThan: opIdx = 2; break; case BinaryOperatorKind.GreaterThanOrEqual: opIdx = 3; sense = !sense; // gte is emitted as !lt break; default: throw ExceptionUtilities.UnexpectedValue(binOp.OperatorKind.OperatorWithLogical()); } if (IsUnsignedBinaryOperator(binOp)) { opIdx += 4; } else if (IsFloat(binOp.OperatorKind)) { opIdx += 8; } EmitBinaryCondOperatorHelper(s_compOpCodes[opIdx], binOp.Left, binOp.Right, sense); return; }
/// <summary> /// The rewrites are as follows: /// /// x++ /// temp = x /// x = temp + 1 /// return temp /// x-- /// temp = x /// x = temp - 1 /// return temp /// ++x /// temp = x + 1 /// x = temp /// return temp /// --x /// temp = x - 1 /// x = temp /// return temp /// /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that /// will be used. The temp is of the same type as x, but the sum/difference may be wider, in which case a /// conversion is required. /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <param name="isPrefix">True for prefix, false for postfix.</param> /// <param name="isIncrement">True for increment, false for decrement.</param> /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns> private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement) { BoundExpression operand = node.Operand; TypeSymbol operandType = operand.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); ConstantValue constantOne; BinaryOperatorKind binaryOperatorKind; MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind); binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); TypeSymbol constantType = compilation.GetSpecialType(constantOne.SpecialType); BoundExpression boundOne = new BoundLiteral( syntax: null, syntaxTree: null, constantValueOpt: constantOne, type: constantType); LocalSymbol tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol); BoundExpression boundTemp = new BoundLocal( syntax: null, syntaxTree: null, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // NOTE: the LHS may have a narrower type than the operator expects, but that // doesn't seem to cause any problems. If a problem does arise, just add an // explicit BoundConversion. BoundExpression newValue = new BoundBinaryOperator( syntax: null, syntaxTree: null, operatorKind: binaryOperatorKind, left: isPrefix ? operand : boundTemp, right: boundOne, constantValueOpt: null, type: constantType); if (constantType != operandType) { newValue = new BoundConversion( syntax: null, syntaxTree: null, operand: newValue, conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric, symbolOpt: null, @checked: false, explicitCastInCode: false, constantValueOpt: null, type: operandType); } ReadOnlyArray<BoundExpression> assignments = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundAssignmentOperator( syntax: null, syntaxTree: null, left: boundTemp, right: isPrefix ? newValue : operand, type: operandType), new BoundAssignmentOperator( syntax: null, syntaxTree: null, left: operand, right: isPrefix ? boundTemp : newValue, type: operandType)); return new BoundSequence( syntax: node.Syntax, syntaxTree: node.SyntaxTree, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(tempSymbol), sideEffects: assignments, value: boundTemp, type: operandType); }
/// <summary> /// Produces opcode for a jump that corresponds to given operation and sense. /// Also produces a reverse opcode - opcode for the same condition with inverted sense. /// </summary> private static ILOpCode CodeForJump(BoundBinaryOperator op, bool sense, out ILOpCode revOpCode) { int opIdx; switch (op.OperatorKind.Operator()) { case BinaryOperatorKind.Equal: revOpCode = !sense ? ILOpCode.Beq : ILOpCode.Bne_un; return sense ? ILOpCode.Beq : ILOpCode.Bne_un; case BinaryOperatorKind.NotEqual: revOpCode = !sense ? ILOpCode.Bne_un : ILOpCode.Beq; return sense ? ILOpCode.Bne_un : ILOpCode.Beq; case BinaryOperatorKind.LessThan: opIdx = 0; break; case BinaryOperatorKind.LessThanOrEqual: opIdx = 1; break; case BinaryOperatorKind.GreaterThan: opIdx = 2; break; case BinaryOperatorKind.GreaterThanOrEqual: opIdx = 3; break; default: throw ExceptionUtilities.UnexpectedValue(op.OperatorKind.Operator()); } if (IsUnsignedBinaryOperator(op)) { opIdx += 2 * IL_OP_CODE_ROW_LENGTH; //unsigned } else if (IsFloat(op.OperatorKind)) { opIdx += 4 * IL_OP_CODE_ROW_LENGTH; //float } int revOpIdx = opIdx; if (!sense) { opIdx += IL_OP_CODE_ROW_LENGTH; //invert op } else { revOpIdx += IL_OP_CODE_ROW_LENGTH; //invert rev } revOpCode = s_condJumpOpCodes[revOpIdx]; return s_condJumpOpCodes[opIdx]; }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundExpression child = node.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { return base.VisitBinaryOperator(node); } // Do not blow the stack due to a deep recursion on the left. var stack = ArrayBuilder<BoundBinaryOperator>.GetInstance(); stack.Push(node); BoundBinaryOperator binary = (BoundBinaryOperator)child; while (true) { stack.Push(binary); child = binary.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { break; } binary = (BoundBinaryOperator)child; } var left = (BoundExpression)this.Visit(child); while (true) { binary = stack.Pop(); var right = (BoundExpression)this.Visit(binary.Right); var type = this.VisitType(binary.Type); left = binary.Update(binary.OperatorKind, left, right, binary.ConstantValueOpt, binary.MethodOpt, binary.ResultKind, type); if (stack.Count == 0) { break; } _nodeCounter += 1; } Debug.Assert((object)binary == node); stack.Free(); return left; }
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 void EmitBinaryOperator(BoundBinaryOperator expression) { BoundExpression child = expression.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { EmitBinaryOperatorSimple(expression); return; } BoundBinaryOperator binary = (BoundBinaryOperator)child; var operatorKind = binary.OperatorKind; if (!operatorKind.EmitsAsCheckedInstruction() && IsConditional(operatorKind)) { EmitBinaryOperatorSimple(expression); return; } // Do not blow the stack due to a deep recursion on the left. var stack = ArrayBuilder<BoundBinaryOperator>.GetInstance(); stack.Push(expression); while (true) { stack.Push(binary); child = binary.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { break; } binary = (BoundBinaryOperator)child; operatorKind = binary.OperatorKind; if (!operatorKind.EmitsAsCheckedInstruction() && IsConditional(operatorKind)) { break; } } EmitExpression(child, true); do { binary = stack.Pop(); EmitExpression(binary.Right, true); bool isChecked = binary.OperatorKind.EmitsAsCheckedInstruction(); if (isChecked) { EmitBinaryCheckedOperatorInstruction(binary); } else { EmitBinaryOperatorInstruction(binary); } EmitConversionToEnumUnderlyingType(binary, @checked: isChecked); } while (stack.Count > 0); Debug.Assert((object)binary == expression); stack.Free(); }
protected override BoundStatement RewriteForStatement(BoundForStatement node) { // for <var> = <lower> to <upper> by <incriment> // <body> // // ----> // // { // var <var> = <lower> // while (<var> <= <upper>) // { // <body> // continue: // <var> = <var> + 1 // } // } var variableDeclaration = new BoundVeriableDeclaration(node.Variable, node.LowerBound); var variableExpression = new BoundVariableExpression(node.Variable); var upperBoundSybmle = new LocalVariableSymbol("upperBound", true, TypeSymbol.Int); var upperBoundDeclaration = new BoundVeriableDeclaration(upperBoundSybmle, node.UpperBound); var condition = new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(SyntaxKind.LessOrEqualToken, TypeSymbol.Int, TypeSymbol.Int), new BoundVariableExpression(upperBoundSybmle) ); var Ittertator = node.Itterator; var Operator = SyntaxKind.PlusToken; if (Ittertator == null) { Ittertator = new BoundLiteralExpression(1); } var continueLabelStatement = new BoundLabelStatement(node.ContinueLabel); var increment = new BoundExpressionStatemnet( new BoundAssignmentExpression( node.Variable, new BoundBinaryExpression( variableExpression, BoundBinaryOperator.Bind(Operator, TypeSymbol.Int, TypeSymbol.Int), Ittertator ) ) ); var whileBody = new BoundBlockStatemnet(ImmutableArray.Create <BoundStatement>( node.Body, continueLabelStatement, increment) ); var whileStatement = new BoundWhileStatement(condition, whileBody, node.BodyLabel, node.BreakLabel, GenerateLabel()); var result = new BoundBlockStatemnet(ImmutableArray.Create <BoundStatement>( variableDeclaration, upperBoundDeclaration, whileStatement)); return(RewriteStatement(result)); }
public BoundBinaryExpression(BoundExpression left, BoundBinaryOperator op, BoundExpression right) { Left = left; Op = op; Right = right; }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.Rank == 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray <BoundExpression> .CreateFrom(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVariableDecl, rewrittenBody)); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, locals: ReadOnlyArray <LocalSymbol> .CreateFrom(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
internal void Parse(BoundBinaryOperator boundBinaryOperator) { base.Parse(boundBinaryOperator); this.OperatorKind = boundBinaryOperator.OperatorKind; // special case for PointerAddition if (IsPointerOperation(this.OperatorKind)) { var left = true; var boundBinaryOperator2 = FindBinaryOperatorForPointerOperation(boundBinaryOperator.Right); if (boundBinaryOperator2 == null) { left = false; boundBinaryOperator2 = FindBinaryOperatorForPointerOperation(boundBinaryOperator.Left); } if (boundBinaryOperator2 != null) { if (HasSizeOfOperator(boundBinaryOperator2.Left)) { this.Right = Deserialize(boundBinaryOperator2.Right) as Expression; } else if (HasSizeOfOperator(boundBinaryOperator2.Right)) { this.Right = Deserialize(boundBinaryOperator2.Left) as Expression; } if (this.Right != null) { this.Left = (left) ? Deserialize(boundBinaryOperator.Left) as Expression : Deserialize(boundBinaryOperator.Right) as Expression; return; } } // to support <pointer> +/- sizeof() if (boundBinaryOperator2 == null && HasSizeOfOperator(boundBinaryOperator.Right)) { this.Left = Deserialize(boundBinaryOperator.Left) as Expression; this.Right = new Literal { Value = ConstantValue.Create(1) }; return; } if (boundBinaryOperator2 == null && HasSizeOfOperator(boundBinaryOperator.Left)) { this.Left = new Literal { Value = ConstantValue.Create(1) }; this.Right = Deserialize(boundBinaryOperator.Right) as Expression; return; } } // special case for PointerSubtraction if (GetOperatorKind(this.OperatorKind) == BinaryOperatorKind.Division) { var boundBinaryOperator2 = boundBinaryOperator.Left as BoundBinaryOperator; if (boundBinaryOperator2 != null && IsPointerOperation(boundBinaryOperator2.OperatorKind) && GetOperatorKind(boundBinaryOperator2.OperatorKind) == BinaryOperatorKind.Subtraction) { this.Parse(boundBinaryOperator2); return; } } this.Left = Deserialize(boundBinaryOperator.Left) as Expression; this.Right = Deserialize(boundBinaryOperator.Right) as Expression; }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { if (node.ConstantValueOpt != null) { return node; } switch (node.OperatorKind) { case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: return RewriteStringConcatenation(node); case BinaryOperatorKind.StringEqual: return RewriteStringEquality(node, SpecialMember.System_String__op_Equality); case BinaryOperatorKind.StringNotEqual: return RewriteStringEquality(node, SpecialMember.System_String__op_Inequality); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(node, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: return RewriteDelegateOperation(node, SpecialMember.System_Delegate__Remove); case BinaryOperatorKind.DelegateEqual: return RewriteDelegateOperation(node, SpecialMember.System_Delegate__op_Equality); case BinaryOperatorKind.DelegateNotEqual: return RewriteDelegateOperation(node, SpecialMember.System_Delegate__op_Inequality); } return base.VisitBinaryOperator(node); }
public BoundBinaryExpression(BoundExpression left, BoundBinaryOperator @operator, BoundExpression right) { Left = left; Operator = @operator; Right = right; }
private void EmitConversionToEnumUnderlyingType(BoundBinaryOperator expression, bool @checked) { // If we are doing an enum addition or subtraction and the // underlying type is 8 or 16 bits then we will have done the operation in 32 // bits and we need to convert back down to the smaller bit size // to [one|zero]extend the value // NOTE: we do not need to do this for bitwise operations since they will always // result in a properly sign-extended result, assuming operands were sign extended // // If e is a value of enum type E and u is a value of underlying type u then: // // e + u --> (E)((U)e + u) // u + e --> (E)(u + (U)e) // e - e --> (U)((U)e - (U)e) // e - u --> (E)((U)e - u) // e & e --> (E)((U)e & (U)e) // e | e --> (E)((U)e | (U)e) // e ^ e --> (E)((U)e ^ (U)e) // // NOTE: (E) is actually emitted as (U) and in last 3 cases is not necessary. // // Due to a bug, the native compiler allows: // // u - e --> (E)(u - (U)e) // // And so Roslyn does as well. TypeSymbol enumType; switch (expression.OperatorKind.Operator() | expression.OperatorKind.OperandTypes()) { case BinaryOperatorKind.EnumAndUnderlyingAddition: case BinaryOperatorKind.EnumSubtraction: case BinaryOperatorKind.EnumAndUnderlyingSubtraction: enumType = expression.Left.Type; break; case BinaryOperatorKind.EnumAnd: case BinaryOperatorKind.EnumOr: case BinaryOperatorKind.EnumXor: Debug.Assert(expression.Left.Type == expression.Right.Type); enumType = null; break; case BinaryOperatorKind.UnderlyingAndEnumSubtraction: case BinaryOperatorKind.UnderlyingAndEnumAddition: enumType = expression.Right.Type; break; default: enumType = null; break; } if ((object)enumType == null) { return; } Debug.Assert(enumType.IsEnumType()); SpecialType type = enumType.GetEnumUnderlyingType().SpecialType; switch (type) { case SpecialType.System_Byte: _builder.EmitNumericConversion(Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.UInt8, @checked); break; case SpecialType.System_SByte: _builder.EmitNumericConversion(Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.Int8, @checked); break; case SpecialType.System_Int16: _builder.EmitNumericConversion(Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.Int16, @checked); break; case SpecialType.System_UInt16: _builder.EmitNumericConversion(Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.UInt16, @checked); break; } }
//NOTE: The result of this should be a boolean on the stack. private void EmitBinaryCondOperator(BoundBinaryOperator binOp, bool sense) { bool andOrSense = sense; int opIdx; switch (binOp.OperatorKind.OperatorWithLogical()) { case BinaryOperatorKind.LogicalOr: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // Rewrite (a || b) as ~(~a && ~b) andOrSense = !andOrSense; // Fall through goto case BinaryOperatorKind.LogicalAnd; case BinaryOperatorKind.LogicalAnd: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // ~(a && b) is equivalent to (~a || ~b) if (!andOrSense) { // generate (~a || ~b) EmitShortCircuitingOperator(binOp, sense, sense, true); } else { // generate (a && b) EmitShortCircuitingOperator(binOp, sense, !sense, false); } return; case BinaryOperatorKind.And: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); EmitBinaryCondOperatorHelper(ILOpCode.And, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.Or: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); EmitBinaryCondOperatorHelper(ILOpCode.Or, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.Xor: Debug.Assert(binOp.Left.Type.SpecialType == SpecialType.System_Boolean); Debug.Assert(binOp.Right.Type.SpecialType == SpecialType.System_Boolean); // Xor is equivalent to not equal. if (sense) { EmitBinaryCondOperatorHelper(ILOpCode.Xor, binOp.Left, binOp.Right, true); } else { EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, true); } return; case BinaryOperatorKind.NotEqual: // neq is emitted as !eq sense = !sense; goto case BinaryOperatorKind.Equal; case BinaryOperatorKind.Equal: var constant = binOp.Left.ConstantValue; var comparand = binOp.Right; if (constant == null) { constant = comparand.ConstantValue; comparand = binOp.Left; } if (constant != null) { if (constant.IsDefaultValue) { if (!constant.IsFloating) { if (sense) { EmitIsNullOrZero(comparand, constant); } else { // obj != null/0 for pointers and integral numerics is emitted as cgt.un EmitIsNotNullOrZero(comparand, constant); } return; } } else if (constant.IsBoolean) { // treat "x = True" ==> "x" EmitExpression(comparand, true); EmitIsSense(sense); return; } } EmitBinaryCondOperatorHelper(ILOpCode.Ceq, binOp.Left, binOp.Right, sense); return; case BinaryOperatorKind.LessThan: opIdx = 0; break; case BinaryOperatorKind.LessThanOrEqual: opIdx = 1; sense = !sense; // lte is emitted as !gt break; case BinaryOperatorKind.GreaterThan: opIdx = 2; break; case BinaryOperatorKind.GreaterThanOrEqual: opIdx = 3; sense = !sense; // gte is emitted as !lt break; default: throw ExceptionUtilities.UnexpectedValue(binOp.OperatorKind.OperatorWithLogical()); } if (IsUnsignedBinaryOperator(binOp)) { opIdx += 4; } else if (IsFloat(binOp.OperatorKind)) { opIdx += 8; } EmitBinaryCondOperatorHelper(s_compOpCodes[opIdx], binOp.Left, binOp.Right, sense); return; }
private DustObject EvaluateBinaryExpression(BoundBinaryExpression binaryExpression) { DustObject left = EvaluateExpression(binaryExpression.Left); DustObject right = EvaluateExpression(binaryExpression.Right); BoundBinaryOperator @operator = binaryExpression.Operator; if (@operator.IsInterchangeable && !left.IsAssignableFrom(@operator.LeftType)) { (left, right) = (right, left); } switch (binaryExpression.Operator.Kind) { case BinaryOperatorKind.Add: return(left.Add(right)); case BinaryOperatorKind.Subtract: return(left.Subtract(right)); case BinaryOperatorKind.Multiply: return(left.Multiply(right)); case BinaryOperatorKind.Divide: return(left.Divide(right)); /*case BinaryOperatorKind.Modulo: * if (left is double || right is double) * { * return (double) left % (double) right; * } * * if (left is float || right is float) * { * return (float) left % (float) right; * } * * return (int) left % (int) right; * case BinaryOperatorKind.Exponentiate: * return Convert.ChangeType(Math.Pow(Convert.ToDouble(left), Convert.ToDouble(right)), binaryExpression.Type.ToNativeType()); * case BinaryOperatorKind.Equal: * { * if (left is bool leftBoolValue && right is bool rightBoolValue) * { * return leftBoolValue == rightBoolValue; * } * * if (left is double || right is double) * { * return (double) left == (double) right; * } * * if (left is float || right is float) * { * return (float) left == (float) right; * } * * if (left is int || right is int) * { * return (int) left == (int) right; * } * * throw new Exception("Equal only implemented for booleans."); * } * case BinaryOperatorKind.NotEqual: * { * if (left is bool leftBoolValue && right is bool rightBoolValue) * { * return leftBoolValue != rightBoolValue; * } * * if (left is double || right is double) * { * return (double) left != (double) right; * } * * if (left is float || right is float) * { * return (float) left != (float) right; * } * * if (left is int || right is int) * { * return (int) left != (int) right; * } * * throw new Exception("NotEqual only implemented for booleans."); * } * case BinaryOperatorKind.And: * return (bool) left && (bool) right; * case BinaryOperatorKind.Or: * return (bool) left || (bool) right; * case BinaryOperatorKind.GreaterThan: * return Convert.ToDouble(left) > Convert.ToDouble(right); * case BinaryOperatorKind.GreaterThanEqual: * return Convert.ToDouble(left) >= Convert.ToDouble(right); * case BinaryOperatorKind.LessThan: * return Convert.ToDouble(left) < Convert.ToDouble(right); * case BinaryOperatorKind.LessThanEqual: * return Convert.ToDouble(left) <= Convert.ToDouble(right);*/ default: return(null); } }
protected override BoundExpression RewriteForExpression(BoundForExpression node) { /* * convert from for to while * * for (x <- l to u) expr * * var x = l * while(x < u) { * expr * continue: * x = x + 1 * } */ var lowerBound = RewriteExpression(node.LowerBound); var upperBound = RewriteExpression(node.UpperBound); var body = RewriteExpression(node.Body); var declareX = new BoundVariableDeclarationStatement( node.Syntax, node.Variable, lowerBound ); var variableExpression = ValueExpression(node.Syntax, node.Variable); var condition = new BoundBinaryExpression( node.Syntax, variableExpression, BoundBinaryOperator.BindOrThrow(SyntaxKind.LessThanToken, Type.Int, Type.Int), upperBound ); var continueLabelStatement = new BoundLabelStatement(node.Syntax, node.ContinueLabel); var incrementX = new BoundExpressionStatement( node.Syntax, new BoundAssignmentExpression( node.Syntax, variableExpression, new BoundBinaryExpression( node.Syntax, variableExpression, BoundBinaryOperator.BindOrThrow(SyntaxKind.PlusToken, Type.Int, Type.Int), new BoundLiteralExpression(node.Syntax, 1) ) ) ); var whileBody = new BoundBlockExpression( node.Syntax, ImmutableArray.Create <BoundStatement>( new BoundExpressionStatement(body.Syntax, body), continueLabelStatement, incrementX ), new BoundUnitExpression(node.Syntax) ); var newBlock = new BoundBlockExpression( node.Syntax, ImmutableArray.Create <BoundStatement>(declareX), new BoundWhileExpression( node.Syntax, condition, whileBody, node.BreakLabel, new BoundLabel("continue") ) ); return(RewriteExpression(newBlock)); }
private static bool IsUnsignedBinaryOperator(BoundBinaryOperator op) { BinaryOperatorKind opKind = op.OperatorKind; BinaryOperatorKind type = opKind.OperandTypes(); switch (type) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: return IsUnsigned(Binder.GetEnumPromotedType(op.Left.Type.GetEnumUnderlyingType().SpecialType)); case BinaryOperatorKind.UnderlyingAndEnum: return IsUnsigned(Binder.GetEnumPromotedType(op.Right.Type.GetEnumUnderlyingType().SpecialType)); case BinaryOperatorKind.UInt: case BinaryOperatorKind.ULong: case BinaryOperatorKind.ULongAndPointer: case BinaryOperatorKind.PointerAndInt: case BinaryOperatorKind.PointerAndUInt: case BinaryOperatorKind.PointerAndLong: case BinaryOperatorKind.PointerAndULong: case BinaryOperatorKind.Pointer: return true; // Dev10 bases signedness on the first operand (see ILGENREC::genOperatorExpr). case BinaryOperatorKind.IntAndPointer: case BinaryOperatorKind.LongAndPointer: // Dev10 converts the uint to a native int, so it counts as signed. case BinaryOperatorKind.UIntAndPointer: default: return false; } }
public BoundBinaryExpression(SyntaxNode syntax, BoundExpression left, BoundBinaryOperator @operator, BoundExpression right) : base(syntax) => (this.Left, this.Operator, this.Right, this.ConstantValue) =
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = new TempLocalSymbol(stringType, RefKind.None, containingMethod); // int p; LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(stringVarDecl, positionVariableDecl)); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_String__Length), arguments: ReadOnlyArray <BoundExpression> .Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.Exists); // (V)s.Chars[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__Chars), arguments: ReadOnlyArray <BoundExpression> .CreateFrom(boundPositionVar)), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody)); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, locals: ReadOnlyArray <LocalSymbol> .CreateFrom(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
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 BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray <BoundExpression> .CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray <BoundExpression> dimensionArgument = ReadOnlyArray <BoundExpression> .CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt : ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type : intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray <LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray <LocalSymbol> .CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray <LocalSymbol> .CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return(forLoop); }
private void EmitBinaryCheckedOperatorExpression(BoundBinaryOperator expression, bool used) { EmitExpression(expression.Left, true); EmitExpression(expression.Right, true); var unsigned = IsUnsignedBinaryOperator(expression); switch (expression.OperatorKind.Operator()) { case BinaryOperatorKind.Multiplication: if (unsigned) { _builder.EmitOpCode(ILOpCode.Mul_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Mul_ovf); } break; case BinaryOperatorKind.Addition: if (unsigned) { _builder.EmitOpCode(ILOpCode.Add_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Add_ovf); } break; case BinaryOperatorKind.Subtraction: if (unsigned) { _builder.EmitOpCode(ILOpCode.Sub_ovf_un); } else { _builder.EmitOpCode(ILOpCode.Sub_ovf); } break; default: throw ExceptionUtilities.UnexpectedValue(expression.OperatorKind.Operator()); } EmitConversionToEnumUnderlyingType(expression, @checked: true); EmitPopIfUnused(used); }
/// <inheritdoc/> protected override BoundStatement RewriteForEllipsisStatement(BoundForEllipsisStatement node) { // for <var> := <lower> ... <upper> // <body> // // ----> // // { // var <var> = <lower> // const upperBound = <upper> // var step = 1 // if <var> greaterthan upperBound { // step = -1 // } // goto start // body: // <body> // continue: // <var> = <var> + step // start: // gotoTrue ((step > 0 && lower < upper) || (step < 0 && lower > upper)) body // break: // } var variableDeclaration = new BoundVariableDeclaration(node.Variable, node.LowerBound); var upperBoundSymbol = new LocalVariableSymbol("upperBound", isReadOnly: true, type: TypeSymbol.Int); var upperBoundDeclaration = new BoundVariableDeclaration(upperBoundSymbol, node.UpperBound); var stepBoundSymbol = new LocalVariableSymbol("step", isReadOnly: false, type: TypeSymbol.Int); var stepBoundDeclaration = new BoundVariableDeclaration( variable: stepBoundSymbol, initializer: new BoundLiteralExpression(1)); var variableExpression = new BoundVariableExpression(node.Variable); var upperBoundExpression = new BoundVariableExpression(upperBoundSymbol); var stepBoundExpression = new BoundVariableExpression(stepBoundSymbol); var ifLowerIsGreaterThanUpperExpression = new BoundBinaryExpression( left: variableExpression, op: BoundBinaryOperator.Bind(SyntaxKind.GreaterToken, TypeSymbol.Int, TypeSymbol.Int), right: upperBoundExpression); var stepBoundAssingment = new BoundExpressionStatement( expression: new BoundAssignmentExpression( variable: stepBoundSymbol, expression: new BoundLiteralExpression(-1))); var ifLowerIsGreaterThanUpperIfStatement = new BoundIfStatement( condition: ifLowerIsGreaterThanUpperExpression, thenStatement: stepBoundAssingment, elseStatement: null); var startLabel = GenerateLabel(); var gotoStart = new BoundGotoStatement(startLabel); var bodyLabel = GenerateLabel(); var bodyLabelStatement = new BoundLabelStatement(bodyLabel); var continueLabelStatement = new BoundLabelStatement(node.ContinueLabel); var increment = new BoundExpressionStatement( expression: new BoundAssignmentExpression( variable: node.Variable, expression: new BoundBinaryExpression( left: variableExpression, op: BoundBinaryOperator.Bind(SyntaxKind.PlusToken, TypeSymbol.Int, TypeSymbol.Int), right: stepBoundExpression))); var startLabelStatement = new BoundLabelStatement(startLabel); var zeroLiteralExpression = new BoundLiteralExpression(0); var stepGreaterThanZeroExpression = new BoundBinaryExpression( left: stepBoundExpression, op: BoundBinaryOperator.Bind(SyntaxKind.GreaterToken, TypeSymbol.Int, TypeSymbol.Int), right: zeroLiteralExpression); var lowerLessThanUpperExpression = new BoundBinaryExpression( left: variableExpression, op: BoundBinaryOperator.Bind(SyntaxKind.LessToken, TypeSymbol.Int, TypeSymbol.Int), right: upperBoundExpression); var positiveStepAndLowerLessThanUpper = new BoundBinaryExpression( left: stepGreaterThanZeroExpression, op: BoundBinaryOperator.Bind(SyntaxKind.AmpersandAmpersandToken, TypeSymbol.Bool, TypeSymbol.Bool), right: lowerLessThanUpperExpression); var stepLessThanZeroExpression = new BoundBinaryExpression( left: stepBoundExpression, op: BoundBinaryOperator.Bind(SyntaxKind.LessToken, TypeSymbol.Int, TypeSymbol.Int), right: zeroLiteralExpression); var lowerGreaterThanUpperExpression = new BoundBinaryExpression( left: variableExpression, op: BoundBinaryOperator.Bind(SyntaxKind.GreaterToken, TypeSymbol.Int, TypeSymbol.Int), right: upperBoundExpression); var negativeStepAndLowerGreaterThanUpper = new BoundBinaryExpression( left: stepLessThanZeroExpression, op: BoundBinaryOperator.Bind(SyntaxKind.AmpersandAmpersandToken, TypeSymbol.Bool, TypeSymbol.Bool), right: lowerGreaterThanUpperExpression); var condition = new BoundBinaryExpression( positiveStepAndLowerLessThanUpper, BoundBinaryOperator.Bind(SyntaxKind.PipePipeToken, TypeSymbol.Bool, TypeSymbol.Bool), negativeStepAndLowerGreaterThanUpper); var gotoTrue = new BoundConditionalGotoStatement(bodyLabel, condition, jumpIfTrue: true); var breakLabelStatement = new BoundLabelStatement(node.BreakLabel); var result = new BoundBlockStatement(ImmutableArray.Create <BoundStatement>( variableDeclaration, upperBoundDeclaration, stepBoundDeclaration, ifLowerIsGreaterThanUpperIfStatement, gotoStart, bodyLabelStatement, node.Body, continueLabelStatement, increment, startLabelStatement, gotoTrue, breakLabelStatement)); return(RewriteStatement(result)); }
private void EmitBinaryArithOperator(BoundBinaryOperator expression) { EmitExpression(expression.Left, true); EmitExpression(expression.Right, true); switch (expression.OperatorKind.Operator()) { case BinaryOperatorKind.Multiplication: _builder.EmitOpCode(ILOpCode.Mul); break; case BinaryOperatorKind.Addition: _builder.EmitOpCode(ILOpCode.Add); break; case BinaryOperatorKind.Subtraction: _builder.EmitOpCode(ILOpCode.Sub); break; case BinaryOperatorKind.Division: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Div_un); } else { _builder.EmitOpCode(ILOpCode.Div); } break; case BinaryOperatorKind.Remainder: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Rem_un); } else { _builder.EmitOpCode(ILOpCode.Rem); } break; case BinaryOperatorKind.LeftShift: _builder.EmitOpCode(ILOpCode.Shl); break; case BinaryOperatorKind.RightShift: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Shr_un); } else { _builder.EmitOpCode(ILOpCode.Shr); } break; case BinaryOperatorKind.And: _builder.EmitOpCode(ILOpCode.And); break; case BinaryOperatorKind.Xor: _builder.EmitOpCode(ILOpCode.Xor); break; case BinaryOperatorKind.Or: _builder.EmitOpCode(ILOpCode.Or); break; default: throw ExceptionUtilities.UnexpectedValue(expression.OperatorKind.Operator()); } EmitConversionToEnumUnderlyingType(expression, @checked: false); }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = new TempLocalSymbol(stringType, RefKind.None, containingMethod); // int p; LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(stringVarDecl, positionVariableDecl)); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_String__Length), arguments: ReadOnlyArray<BoundExpression>.Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.Exists); // (V)s.Chars[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_String__Chars), arguments: ReadOnlyArray<BoundExpression>.CreateFrom(boundPositionVar)), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private void EmitBinaryOperatorInstruction(BoundBinaryOperator expression) { switch (expression.OperatorKind.Operator()) { case BinaryOperatorKind.Multiplication: _builder.EmitOpCode(ILOpCode.Mul); break; case BinaryOperatorKind.Addition: _builder.EmitOpCode(ILOpCode.Add); break; case BinaryOperatorKind.Subtraction: _builder.EmitOpCode(ILOpCode.Sub); break; case BinaryOperatorKind.Division: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Div_un); } else { _builder.EmitOpCode(ILOpCode.Div); } break; case BinaryOperatorKind.Remainder: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Rem_un); } else { _builder.EmitOpCode(ILOpCode.Rem); } break; case BinaryOperatorKind.LeftShift: _builder.EmitOpCode(ILOpCode.Shl); break; case BinaryOperatorKind.RightShift: if (IsUnsignedBinaryOperator(expression)) { _builder.EmitOpCode(ILOpCode.Shr_un); } else { _builder.EmitOpCode(ILOpCode.Shr); } break; case BinaryOperatorKind.And: _builder.EmitOpCode(ILOpCode.And); break; case BinaryOperatorKind.Xor: _builder.EmitOpCode(ILOpCode.Xor); break; case BinaryOperatorKind.Or: _builder.EmitOpCode(ILOpCode.Or); break; default: throw ExceptionUtilities.UnexpectedValue(expression.OperatorKind.Operator()); } }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.Rank == 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = new TempLocalSymbol(intType, RefKind.None, containingMethod); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, new BoundLiteral(forEachSyntax, ConstantValue.ConstantValueZero.Int32, intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVariableDecl, rewrittenBody)); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntax: forEachSyntax.InKeyword, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray<BoundExpression> dimensionArgument = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray<LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray<LocalSymbol>.CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if(forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return forLoop; }
private void EmitBinaryOperator(BoundBinaryOperator expression) { BoundExpression child = expression.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { EmitBinaryOperatorSimple(expression); return; } BoundBinaryOperator binary = (BoundBinaryOperator)child; var operatorKind = binary.OperatorKind; if (!operatorKind.EmitsAsCheckedInstruction() && IsConditional(operatorKind)) { EmitBinaryOperatorSimple(expression); return; } // Do not blow the stack due to a deep recursion on the left. var stack = ArrayBuilder <BoundBinaryOperator> .GetInstance(); stack.Push(expression); while (true) { stack.Push(binary); child = binary.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { break; } binary = (BoundBinaryOperator)child; operatorKind = binary.OperatorKind; if (!operatorKind.EmitsAsCheckedInstruction() && IsConditional(operatorKind)) { break; } } EmitExpression(child, true); do { binary = stack.Pop(); EmitExpression(binary.Right, true); bool isChecked = binary.OperatorKind.EmitsAsCheckedInstruction(); if (isChecked) { EmitBinaryCheckedOperatorInstruction(binary); } else { EmitBinaryOperatorInstruction(binary); } EmitConversionToEnumUnderlyingType(binary, @checked: isChecked); }while (stack.Count > 0); Debug.Assert((object)binary == expression); stack.Free(); }
private void EmitBinaryOperatorSimple(BoundBinaryOperator expression) { EmitExpression(expression.Left, true); EmitExpression(expression.Right, true); bool isChecked = expression.OperatorKind.EmitsAsCheckedInstruction(); if (isChecked) { EmitBinaryCheckedOperatorInstruction(expression); } else { EmitBinaryOperatorInstruction(expression); } EmitConversionToEnumUnderlyingType(expression, @checked: isChecked); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundExpression child = node.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { return VisitBinaryOperatorSimple(node); } // Do not blow the stack due to a deep recursion on the left. var stack = ArrayBuilder<BoundBinaryOperator>.GetInstance(); stack.Push(node); BoundBinaryOperator binary = (BoundBinaryOperator)child; while (true) { stack.Push(binary); child = binary.Left; if (child.Kind != BoundKind.BinaryOperator || child.ConstantValue != null) { break; } binary = (BoundBinaryOperator)child; } var prevContext = _context; int prevStack = StackDepth(); var left = (BoundExpression)this.Visit(child); while (true) { binary = stack.Pop(); var isLogical = (binary.OperatorKind & BinaryOperatorKind.Logical) != 0; object cookie = null; if (isLogical) { cookie = GetStackStateCookie(); // implicit branch here SetStackDepth(prevStack); // right is evaluated with original stack } var right = (BoundExpression)this.Visit(binary.Right); if (isLogical) { EnsureStackState(cookie); // implicit label here } var type = this.VisitType(binary.Type); left = binary.Update(binary.OperatorKind, left, right, binary.ConstantValueOpt, binary.MethodOpt, binary.ResultKind, type); if (stack.Count == 0) { break; } _context = prevContext; _counter += 1; SetStackDepth(prevStack); PushEvalStack(binary, ExprContext.Value); } Debug.Assert((object)binary == node); stack.Free(); return left; }
private static BoundExpression TryReduce(BoundBinaryOperator condition, ref bool sense) { var opKind = condition.OperatorKind.Operator(); Debug.Assert(opKind == BinaryOperatorKind.Equal || opKind == BinaryOperatorKind.NotEqual); BoundExpression nonConstOp; BoundExpression constOp = (condition.Left.ConstantValue != null) ? condition.Left : null; if (constOp != null) { nonConstOp = condition.Right; } else { constOp = (condition.Right.ConstantValue != null) ? condition.Right : null; if (constOp == null) { return null; } nonConstOp = condition.Left; } var nonConstType = nonConstOp.Type; if (!CanPassToBrfalse(nonConstType)) { return null; } bool isBool = nonConstType.PrimitiveTypeCode == Microsoft.Cci.PrimitiveTypeCode.Boolean; bool isZero = constOp.ConstantValue.IsDefaultValue; // bool is special, only it can be compared to true and false... if (!isBool && !isZero) { return null; } // if comparing to zero, flip the sense if (isZero) { sense = !sense; } // if comparing != flip the sense if (opKind == BinaryOperatorKind.NotEqual) { sense = !sense; } return nonConstOp; }
private void EmitShortCircuitingOperator(BoundBinaryOperator condition, bool sense, bool stopSense, bool stopValue) { // we generate: // // gotoif (a == stopSense) fallThrough // b == sense // goto labEnd // fallThrough: // stopValue // labEnd: // AND OR // +- ------ ----- // stopSense | !sense sense // stopValue | 0 1 object lazyFallThrough = null; EmitCondBranch(condition.Left, ref lazyFallThrough, stopSense); EmitCondExpr(condition.Right, sense); // if fall-through was not initialized, no-one is going to take that branch // and we are done with Right on stack if (lazyFallThrough == null) { return; } var labEnd = new object(); _builder.EmitBranch(ILOpCode.Br, labEnd); // if we get to fallThrough, we should not have Right on stack. Adjust for that. _builder.AdjustStack(-1); _builder.MarkLabel(lazyFallThrough); _builder.EmitBoolConstant(stopValue); _builder.MarkLabel(labEnd); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { var isLogical = (node.OperatorKind & BinaryOperatorKind.Logical) != 0; if (isLogical) { var origStack = StackDepth(); BoundExpression left = (BoundExpression)this.Visit(node.Left); var cookie = GetStackStateCookie(); // implicit branch here SetStackDepth(origStack); // right is evaluated with original stack BoundExpression right = (BoundExpression)this.Visit(node.Right); EnsureStackState(cookie); // implicit label here return node.Update(node.OperatorKind, left, right, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, node.Type); } return base.VisitBinaryOperator(node); }
/// <summary> /// The rewrites are as follows: /// /// x++ /// temp = x /// x = temp + 1 /// return temp /// x-- /// temp = x /// x = temp - 1 /// return temp /// ++x /// temp = x + 1 /// x = temp /// return temp /// --x /// temp = x - 1 /// x = temp /// return temp /// /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that /// will be used. The temp is of the same type as x, but the sum/difference may be wider, in which case a /// conversion is required. /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <param name="isPrefix">True for prefix, false for postfix.</param> /// <param name="isIncrement">True for increment, false for decrement.</param> /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns> private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement) { BoundExpression operand = node.Operand; TypeSymbol operandType = operand.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); ConstantValue constantOne; BinaryOperatorKind binaryOperatorKind; MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind); binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); TypeSymbol constantType = compilation.GetSpecialType(constantOne.SpecialType); BoundExpression boundOne = new BoundLiteral( syntax: null, syntaxTree: null, constantValueOpt: constantOne, type: constantType); LocalSymbol tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol); BoundExpression boundTemp = new BoundLocal( syntax: null, syntaxTree: null, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // NOTE: the LHS may have a narrower type than the operator expects, but that // doesn't seem to cause any problems. If a problem does arise, just add an // explicit BoundConversion. BoundExpression newValue = new BoundBinaryOperator( syntax: null, syntaxTree: null, operatorKind: binaryOperatorKind, left: isPrefix ? operand : boundTemp, right: boundOne, constantValueOpt: null, type: constantType); if (constantType != operandType) { newValue = new BoundConversion( syntax: null, syntaxTree: null, operand: newValue, conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric, symbolOpt: null, @checked: false, explicitCastInCode: false, constantValueOpt: null, type: operandType); } ReadOnlyArray <BoundExpression> assignments = ReadOnlyArray <BoundExpression> .CreateFrom( new BoundAssignmentOperator( syntax : null, syntaxTree : null, left : boundTemp, right : isPrefix ? newValue : operand, type : operandType), new BoundAssignmentOperator( syntax : null, syntaxTree : null, left : operand, right : isPrefix ? boundTemp : newValue, type : operandType)); return(new BoundSequence( syntax: node.Syntax, syntaxTree: node.SyntaxTree, locals: ReadOnlyArray <LocalSymbol> .CreateFrom(tempSymbol), sideEffects: assignments, value: boundTemp, type: operandType)); }