private void EmitBranchComparison(bool branch, BinaryExpression node, Label label) { Debug.Assert(node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual); Debug.Assert(!node.IsLiftedToNull); // To share code paths, we want to treat NotEqual as an inverted Equal var branchWhenEqual = branch == (node.NodeType == ExpressionType.Equal); if (node.Method != null) { EmitBinaryMethod(node, CompilationFlags.EmitAsNoTail); // EmitBinaryMethod takes into account the Equal/NotEqual // node kind, so use the original branch value EmitBranchOp(branch, label); } else if (ConstantCheck.IsNull(node.Left)) { if (node.Right.Type.IsNullable()) { EmitAddress(node.Right, node.Right.Type); IL.EmitHasValue(node.Right.Type); } else { Debug.Assert(!node.Right.Type.IsValueType); EmitExpression(GetEqualityOperand(node.Right)); } EmitBranchOp(!branchWhenEqual, label); } else if (ConstantCheck.IsNull(node.Right)) { if (node.Left.Type.IsNullable()) { EmitAddress(node.Left, node.Left.Type); IL.EmitHasValue(node.Left.Type); } else { Debug.Assert(!node.Left.Type.IsValueType); EmitExpression(GetEqualityOperand(node.Left)); } EmitBranchOp(!branchWhenEqual, label); } else if (node.Left.Type.IsNullable() || node.Right.Type.IsNullable()) { EmitBinaryExpression(node); // EmitBinaryExpression takes into account the Equal/NotEqual // node kind, so use the original branch value EmitBranchOp(branch, label); } else { EmitExpression(GetEqualityOperand(node.Left)); EmitExpression(GetEqualityOperand(node.Right)); IL.Emit(branchWhenEqual ? OpCodes.Beq : OpCodes.Bne_Un, label); } }
private void EmitBinaryExpression(Expression expr, CompilationFlags flags = CompilationFlags.EmitAsNoTail) { var b = (BinaryExpression)expr; Debug.Assert(b.NodeType != ExpressionType.AndAlso && b.NodeType != ExpressionType.OrElse && b.NodeType != ExpressionType.Coalesce); if (b.Method != null) { EmitBinaryMethod(b, flags); return; } // For EQ and NE, if there is a user-specified method, use it. // Otherwise implement the C# semantics that allow equality // comparisons on non-primitive nullable structs that don't // overload "==" if ( (b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual) && (b.Type == typeof(bool) || b.Type == typeof(bool?)) ) { // If we have x==null, x!=null, null==x or null!=x where x is // nullable but not null, then generate a call to x.HasValue. Debug.Assert(!b.IsLiftedToNull || b.Type == typeof(bool?)); if (ConstantCheck.IsNull(b.Left) && !ConstantCheck.IsNull(b.Right) && b.Right.Type.IsNullable()) { EmitNullEquality(b.NodeType, b.Right, b.IsLiftedToNull); return; } if (ConstantCheck.IsNull(b.Right) && !ConstantCheck.IsNull(b.Left) && b.Left.Type.IsNullable()) { EmitNullEquality(b.NodeType, b.Left, b.IsLiftedToNull); return; } // For EQ and NE, we can avoid some conversions if we're // ultimately just comparing two managed pointers. EmitExpression(GetEqualityOperand(b.Left)); EmitExpression(GetEqualityOperand(b.Right)); } else { // Otherwise generate it normally EmitExpression(b.Left); EmitExpression(b.Right); } EmitBinaryOperator(b.NodeType, b.Left.Type, b.Right.Type, b.Type, b.IsLiftedToNull); }
//CONFORMING private void EmitBinaryExpression(Expression expr) { BinaryExpression b = (BinaryExpression)expr; Debug.Assert(b.NodeType != ExpressionType.AndAlso && b.NodeType != ExpressionType.OrElse && b.NodeType != ExpressionType.Coalesce); if (b.Method != null) { EmitBinaryMethod(b); return; } // For EQ and NE, if there is a user-specified method, use it. // Otherwise implement the C# semantics that allow equality // comparisons on non-primitive nullable structs that don't // overload "==" if ((b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual) && (b.Type == typeof(bool) || b.Type == typeof(bool?))) { // If we have x==null, x!=null, null==x or null!=x where x is // nullable but not null, then generate a call to x.HasValue. Debug.Assert(!b.IsLiftedToNull || b.Type == typeof(bool?)); if (ConstantCheck.IsNull(b.Left) && !ConstantCheck.IsNull(b.Right) && TypeUtils.IsNullableType(b.Right.Type)) { EmitNullEquality(b.NodeType, b.Right, b.IsLiftedToNull); return; } if (ConstantCheck.IsNull(b.Right) && !ConstantCheck.IsNull(b.Left) && TypeUtils.IsNullableType(b.Left.Type)) { EmitNullEquality(b.NodeType, b.Left, b.IsLiftedToNull); return; } } // Otherwise generate it normally. EmitExpression(b.Left); EmitExpression(b.Right); EmitBinaryOperator(b.NodeType, b.Left.Type, b.Right.Type, b.Type, b.IsLiftedToNull); }
private void EmitBranchComparison(bool branch, BinaryExpression node, Label label) { Debug.Assert(node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual); Debug.Assert(!node.IsLiftedToNull); // To share code paths, we want to treat NotEqual as an inverted Equal bool branchWhenEqual = branch == (node.NodeType == ExpressionType.Equal); if (node.Method != null) { EmitBinaryMethod(node); // EmitBinaryMethod takes into account the Equal/NotEqual // node kind, so use the original branch value EmitBranchOp(branch, label); } else if (ConstantCheck.IsNull(node.Left)) { if (TypeUtils.IsNullableType(node.Right.Type)) { EmitAddress(node.Right, node.Right.Type); _ilg.EmitHasValue(node.Right.Type); } else { Debug.Assert(!node.Right.Type.IsValueType); EmitExpression(node.Right); } EmitBranchOp(!branchWhenEqual, label); } else if (ConstantCheck.IsNull(node.Right)) { if (TypeUtils.IsNullableType(node.Left.Type)) { EmitAddress(node.Left, node.Left.Type); _ilg.EmitHasValue(node.Left.Type); } else { Debug.Assert(!node.Left.Type.IsValueType); EmitExpression(node.Left); } EmitBranchOp(!branchWhenEqual, label); } else if (TypeUtils.IsNullableType(node.Left.Type) || TypeUtils.IsNullableType(node.Right.Type)) { EmitBinaryExpression(node); // EmitBinaryExpression takes into account the Equal/NotEqual // node kind, so use the original branch value EmitBranchOp(branch, label); } else { EmitExpression(node.Left); EmitExpression(node.Right); if (branchWhenEqual) { _ilg.Emit(OpCodes.Beq, label); } else { _ilg.Emit(OpCodes.Ceq); _ilg.Emit(OpCodes.Brfalse, label); } } }