@@ -13117,97 +13117,92 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithCasts(GenTreeOp* cmp)
13117
13117
GenTree* op1 = cmp->gtGetOp1();
13118
13118
GenTree* op2 = cmp->gtGetOp2();
13119
13119
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
13121
13121
assert(op1->OperIs(GT_CAST) || op2->OperIs(GT_CAST));
13122
13122
13123
+ assert(genActualType(op1) == genActualType(op2));
13124
+
13123
13125
if (!op1->TypeIs(TYP_LONG))
13124
13126
{
13125
- // We can extend this logic to handle small types as well, but currently it's done mostly to
13126
- // assist range check elimination
13127
13127
return cmp;
13128
13128
}
13129
13129
13130
- GenTree* castOp;
13131
- GenTree* knownPositiveOp;
13130
+ auto supportedOp = [](GenTree* op) {
13131
+ if (op->IsIntegralConst())
13132
+ {
13133
+ return true;
13134
+ }
13132
13135
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))
13142
13156
{
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;
13148
13158
}
13149
13159
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())
13155
13162
{
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);
13164
13165
}
13165
13166
13166
- if (!knownPositiveFitsIntoU32)
13167
+ assert(op->OperIs(GT_CAST));
13168
+ if (op->AsCast()->IsUnsigned())
13167
13169
{
13168
- return cmp ;
13170
+ return true ;
13169
13171
}
13170
13172
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
+ {
13171
13180
JITDUMP("Removing redundant cast(s) for:\n")
13172
13181
DISPTREE(cmp)
13173
13182
JITDUMP("\n\nto:\n\n")
13174
13183
13175
13184
cmp->SetUnsigned();
13176
13185
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())
13192
13188
{
13193
- cmp->gtOp2 = knownPositiveOp->AsCast()->CastOp();
13189
+ (*use)->BashToConst(static_cast<int>((*use)->AsIntConCommon()->LngValue()));
13190
+ fgUpdateConstTreeValueNumber(*use);
13194
13191
}
13195
13192
else
13196
13193
{
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);
13198
13198
}
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
+
13211
13206
DISPTREE(cmp)
13212
13207
JITDUMP("\n")
13213
13208
}
0 commit comments