Skip to content

Commit c2e6bff

Browse files
adamperlinAdam Perlin
andauthored
JIT: Add more specific range assertion for bitwise and/or (#120980)
This is an attempt to address #119962 by adding a new range assertion for `x & y` when a range for `x` and `y` can be determined. As part of the change, this PR also adds a non-negative range assertion for GT_CNS_INT when appropriate. --------- Co-authored-by: Adam Perlin <[email protected]>
1 parent e3f6dd9 commit c2e6bff

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,27 @@ bool IntegralRange::Contains(int64_t value) const
156156
case GT_GT:
157157
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};
158158

159+
case GT_AND:
160+
{
161+
IntegralRange leftRange = IntegralRange::ForNode(node->gtGetOp1(), compiler);
162+
IntegralRange rightRange = IntegralRange::ForNode(node->gtGetOp2(), compiler);
163+
if (leftRange.IsNonNegative() && rightRange.IsNonNegative())
164+
{
165+
// If both sides are known to be non-negative, the result is non-negative.
166+
// Further, the top end of the range cannot exceed the min of the two upper bounds.
167+
return {SymbolicIntegerValue::Zero, min(leftRange.GetUpperBound(), rightRange.GetUpperBound())};
168+
}
169+
170+
if (leftRange.IsNonNegative() || rightRange.IsNonNegative())
171+
{
172+
// If only one side is known to be non-negative, however it is harder to
173+
// reason about the upper bound.
174+
return {SymbolicIntegerValue::Zero, UpperBoundForType(rangeType)};
175+
}
176+
177+
break;
178+
}
179+
159180
case GT_ARR_LENGTH:
160181
case GT_MDARR_LENGTH:
161182
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::ArrayLenMax};
@@ -215,11 +236,20 @@ bool IntegralRange::Contains(int64_t value) const
215236
}
216237

217238
case GT_CNS_INT:
239+
{
218240
if (node->IsIntegralConst(0) || node->IsIntegralConst(1))
219241
{
220242
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};
221243
}
244+
245+
int64_t constValue = node->AsIntCon()->IntegralValue();
246+
if (constValue >= 0)
247+
{
248+
return {SymbolicIntegerValue::Zero, UpperBoundForType(rangeType)};
249+
}
250+
222251
break;
252+
}
223253

224254
case GT_QMARK:
225255
return Union(ForNode(node->AsQmark()->ThenNode(), compiler),

src/tests/JIT/opt/And/IntAnd.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,15 @@ static bool Test_UInt32_UInt32_CastByte_And(uint x, uint y)
4949
[MethodImpl(MethodImplOptions.NoInlining)]
5050
static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y)
5151
{
52-
// X64-NOT: movzx
53-
54-
// We expect 'and reg8, reg8'.
55-
// X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}}
52+
// We expect 'test reg32, reg32' here. Previously, we expected
53+
// 'and reg8, reg8' due to the outer (byte) cast optimizing the inner casts away.
54+
// Recent changes to codegen allow removing the outer (byte) cast here,
55+
// but will leave the inner casts. Thus, we expect to see movzx instructions for both casts, and a test instruction
56+
// operating on reg32s.
57+
58+
// X64: movzx {{[a-z]+[x|i|p|d]}}, {{[a-z]+[l|b]}}
59+
// X64: movzx {{[a-z]+[x|i|p|d]}}, {{[a-z]+[l|b]}}
60+
// X64: test {{[a-z]+[x|i|p|d]}}, {{[a-z]+[x|i|p|d]}}
5661

5762
if ((byte)((byte)x & (byte)y) == 0)
5863
{

0 commit comments

Comments
 (0)