Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,27 @@ bool IntegralRange::Contains(int64_t value) const
case GT_GT:
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};

case GT_AND:
{
IntegralRange leftRange = IntegralRange::ForNode(node->gtGetOp1(), compiler);
IntegralRange rightRange = IntegralRange::ForNode(node->gtGetOp2(), compiler);
if (leftRange.IsNonNegative() && rightRange.IsNonNegative())
{
// If both sides are known to be non-negative, the result is non-negative.
// Further, the top end of the range cannot exceed the min of the two upper bounds.
return {SymbolicIntegerValue::Zero, min(leftRange.GetUpperBound(), rightRange.GetUpperBound())};
}

if (leftRange.IsNonNegative() || rightRange.IsNonNegative())
{
// If only one side is known to be non-negative, however it is harder to
// reason about the upper bound.
return {SymbolicIntegerValue::Zero, UpperBoundForType(rangeType)};
}

break;
}

case GT_ARR_LENGTH:
case GT_MDARR_LENGTH:
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::ArrayLenMax};
Expand Down Expand Up @@ -215,11 +236,20 @@ bool IntegralRange::Contains(int64_t value) const
}

case GT_CNS_INT:
{
if (node->IsIntegralConst(0) || node->IsIntegralConst(1))
{
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};
}

int64_t constValue = node->AsIntCon()->IntegralValue();
if (constValue >= 0)
{
return {SymbolicIntegerValue::Zero, UpperBoundForType(rangeType)};
}

break;
}

case GT_QMARK:
return Union(ForNode(node->AsQmark()->ThenNode(), compiler),
Expand Down
13 changes: 9 additions & 4 deletions src/tests/JIT/opt/And/IntAnd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ static bool Test_UInt32_UInt32_CastByte_And(uint x, uint y)
[MethodImpl(MethodImplOptions.NoInlining)]
static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y)
{
// X64-NOT: movzx

// We expect 'and reg8, reg8'.
// X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}}
// We expect 'test reg32, reg32' here. Previously, we expected
// 'and reg8, reg8' due to the outer (byte) cast optimizing the inner casts away.
// Recent changes to codegen allow removing the outer (byte) cast here,
// but will leave the inner casts. Thus, we expect to see movzx instructions for both casts, and a test instruction
// operating on reg32s.

// X64: movzx {{[a-z]+[x|i|p|d]}}, {{[a-z]+[l|b]}}
// X64: movzx {{[a-z]+[x|i|p|d]}}, {{[a-z]+[l|b]}}
// X64: test {{[a-z]+[x|i|p|d]}}, {{[a-z]+[x|i|p|d]}}

if ((byte)((byte)x & (byte)y) == 0)
{
Expand Down
Loading