Skip to content

Commit 534cfe3

Browse files
authored
Use IntegralRange in fgOptimizeRelationalComparisonWithCasts (dotnet#69247)
Fix dotnet#69200
1 parent c1b153a commit 534cfe3

File tree

2 files changed

+59
-64
lines changed

2 files changed

+59
-64
lines changed

src/coreclr/jit/gentree.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -3577,7 +3577,7 @@ struct GenTreeLclFld : public GenTreeLclVarCommon
35773577
// -> FP" cases. For all other unchecked casts, "IsUnsigned" is meaningless.
35783578
// 4) For unchecked casts, signedness of the target type is only meaningful if
35793579
// the cast is to an FP or small type. In the latter case (and everywhere
3580-
// else in IR) it decided whether the value will be sign- or zero-extended.
3580+
// else in IR) it decides whether the value will be sign- or zero-extended.
35813581
//
35823582
// For additional context on "GT_CAST"'s semantics, see "IntegralRange::ForCast"
35833583
// methods and "GenIntCastDesc"'s constructor.

src/coreclr/jit/morph.cpp

+58-63
Original file line numberDiff line numberDiff line change
@@ -13117,97 +13117,92 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp)
1311713117
GenTree* op1 = cmp->gtGetOp1();
1311813118
GenTree* op2 = cmp->gtGetOp2();
1311913119

13120-
// Caller is expected to call this function only if we have CAST nodes
13120+
// Caller is expected to call this function only if we have at least one CAST node
1312113121
assert(op1->OperIs(GT_CAST) || op2->OperIs(GT_CAST));
1312213122

13123+
assert(genActualType(op1) == genActualType(op2));
13124+
1312313125
if (!op1->TypeIs(TYP_LONG))
1312413126
{
13125-
// We can extend this logic to handle small types as well, but currently it's done mostly to
13126-
// assist range check elimination
1312713127
return cmp;
1312813128
}
1312913129

13130-
GenTree* castOp;
13131-
GenTree* knownPositiveOp;
13130+
auto supportedOp = [](GenTree* op) {
13131+
if (op->IsIntegralConst())
13132+
{
13133+
return true;
13134+
}
1313213135

13133-
bool knownPositiveIsOp2;
13134-
if (op2->IsIntegralConst() || ((op2->OperIs(GT_CAST) && op2->AsCast()->CastOp()->OperIs(GT_ARR_LENGTH))))
13135-
{
13136-
// op2 is either a LONG constant or (T)ARR_LENGTH
13137-
knownPositiveIsOp2 = true;
13138-
castOp = cmp->gtGetOp1();
13139-
knownPositiveOp = cmp->gtGetOp2();
13140-
}
13141-
else
13136+
if (op->OperIs(GT_CAST))
13137+
{
13138+
if (op->gtOverflow())
13139+
{
13140+
return false;
13141+
}
13142+
13143+
if (genActualType(op->CastFromType()) != TYP_INT)
13144+
{
13145+
return false;
13146+
}
13147+
13148+
assert(varTypeIsLong(op->CastToType()));
13149+
return true;
13150+
}
13151+
13152+
return false;
13153+
};
13154+
13155+
if (!supportedOp(op1) || !supportedOp(op2))
1314213156
{
13143-
// op1 is either a LONG constant (yes, it's pretty normal for relops)
13144-
// or (T)ARR_LENGTH
13145-
castOp = cmp->gtGetOp2();
13146-
knownPositiveOp = cmp->gtGetOp1();
13147-
knownPositiveIsOp2 = false;
13157+
return cmp;
1314813158
}
1314913159

13150-
if (castOp->OperIs(GT_CAST) && varTypeIsLong(castOp->CastToType()) && castOp->AsCast()->CastOp()->TypeIs(TYP_INT) &&
13151-
castOp->IsUnsigned() && !castOp->gtOverflow())
13152-
{
13153-
bool knownPositiveFitsIntoU32 = false;
13154-
if (knownPositiveOp->IsIntegralConst() && FitsIn<UINT32>(knownPositiveOp->AsIntConCommon()->IntegralValue()))
13160+
auto isUpperZero = [this](GenTree* op) {
13161+
if (op->IsIntegralConst())
1315513162
{
13156-
// BTW, we can fold the whole condition if op2 doesn't fit into UINT_MAX.
13157-
knownPositiveFitsIntoU32 = true;
13158-
}
13159-
else if (knownPositiveOp->OperIs(GT_CAST) && varTypeIsLong(knownPositiveOp->CastToType()) &&
13160-
knownPositiveOp->AsCast()->CastOp()->OperIs(GT_ARR_LENGTH))
13161-
{
13162-
knownPositiveFitsIntoU32 = true;
13163-
// TODO-Casts: recognize Span.Length here as well.
13163+
int64_t lng = op->AsIntConCommon()->LngValue();
13164+
return (lng >= 0) && (lng <= UINT_MAX);
1316413165
}
1316513166

13166-
if (!knownPositiveFitsIntoU32)
13167+
assert(op->OperIs(GT_CAST));
13168+
if (op->AsCast()->IsUnsigned())
1316713169
{
13168-
return cmp;
13170+
return true;
1316913171
}
1317013172

13173+
return IntegralRange::ForNode(op->AsCast()->CastOp(), this).IsPositive();
13174+
};
13175+
13176+
// If both operands have zero as the upper half then any signed/unsigned
13177+
// 64-bit comparison is equivalent to the same unsigned 32-bit comparison.
13178+
if (isUpperZero(op1) && isUpperZero(op2))
13179+
{
1317113180
JITDUMP("Removing redundant cast(s) for:\n")
1317213181
DISPTREE(cmp)
1317313182
JITDUMP("\n\nto:\n\n")
1317413183

1317513184
cmp->SetUnsigned();
1317613185

13177-
// Drop cast from castOp
13178-
if (knownPositiveIsOp2)
13179-
{
13180-
cmp->gtOp1 = castOp->AsCast()->CastOp();
13181-
}
13182-
else
13183-
{
13184-
cmp->gtOp2 = castOp->AsCast()->CastOp();
13185-
}
13186-
DEBUG_DESTROY_NODE(castOp);
13187-
13188-
if (knownPositiveOp->OperIs(GT_CAST))
13189-
{
13190-
// Drop cast from knownPositiveOp too
13191-
if (knownPositiveIsOp2)
13186+
auto transform = [this](GenTree** use) {
13187+
if ((*use)->IsIntegralConst())
1319213188
{
13193-
cmp->gtOp2 = knownPositiveOp->AsCast()->CastOp();
13189+
(*use)->BashToConst(static_cast<int>((*use)->AsIntConCommon()->LngValue()));
13190+
fgUpdateConstTreeValueNumber(*use);
1319413191
}
1319513192
else
1319613193
{
13197-
cmp->gtOp1 = knownPositiveOp->AsCast()->CastOp();
13194+
assert((*use)->OperIs(GT_CAST));
13195+
GenTreeCast* cast = (*use)->AsCast();
13196+
*use = cast->CastOp();
13197+
DEBUG_DESTROY_NODE(cast);
1319813198
}
13199-
DEBUG_DESTROY_NODE(knownPositiveOp);
13200-
}
13201-
else
13202-
{
13203-
// Change type for constant from LONG to INT
13204-
knownPositiveOp->ChangeType(TYP_INT);
13205-
#ifndef TARGET_64BIT
13206-
assert(knownPositiveOp->OperIs(GT_CNS_LNG));
13207-
knownPositiveOp->BashToConst(static_cast<int>(knownPositiveOp->AsIntConCommon()->IntegralValue()));
13208-
#endif
13209-
fgUpdateConstTreeValueNumber(knownPositiveOp);
13210-
}
13199+
};
13200+
13201+
transform(&cmp->gtOp1);
13202+
transform(&cmp->gtOp2);
13203+
13204+
assert((genActualType(cmp->gtOp1) == TYP_INT) && (genActualType(cmp->gtOp2) == TYP_INT));
13205+
1321113206
DISPTREE(cmp)
1321213207
JITDUMP("\n")
1321313208
}

0 commit comments

Comments
 (0)