Skip to content

Commit be9df76

Browse files
LotendanLotendan
andauthored
Extend the "skip-test" optimization to all instructions that modify the (#120876)
Extend the "skip-test" optimization to all instructions that modify the same flags as "TEST" x86 instruction Originally wanted to only cover the "POPCNT" case, but actually we can go further than that and cover more instructions. Fixes #118811 Using the following code: ```csharp static void M1(int value) { if (int.PopCount(value) > 0) throw null!; } ``` This fix reduces this assembly: ```asm popcnt edi, edi test edi, edi jg SHORT G_M9581_IG04 ``` to this assembly: ```asm popcnt edi, edi jg SHORT G_M000_IG04 ``` Note: this is not exactly as was intended in the issue, where a `jne` was expected. But that should be good enough. Note2: this does not affect `tnzcnt` or `lzcnt` instructions as these instructions do not modify the flags. Co-authored-by: Lotendan <letupertinois@hotmail.fr>
1 parent 78315b9 commit be9df76

File tree

1 file changed

+11
-2
lines changed

1 file changed

+11
-2
lines changed

src/coreclr/jit/morph.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9196,28 +9196,37 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp)
91969196
oper = GT_GE;
91979197
}
91989198
}
9199-
else if (cmp->IsUnsigned())
9199+
// Check for cases where the comparison is unsigned
9200+
// We can apply the same transformations if the comparison is signed but we know
9201+
// the left expression is never negative
9202+
else if (cmp->IsUnsigned() || op1->IsNeverNegative(this))
92009203
{
92019204
if ((oper == GT_LE) || (oper == GT_GT))
92029205
{
92039206
if (op2Value == 0)
92049207
{
9208+
// Unsigned case:
92059209
// IL doesn't have a cne instruction so compilers use cgt.un instead. The JIT
92069210
// recognizes certain patterns that involve GT_NE (e.g (x & 4) != 0) and fails
92079211
// if GT_GT is used instead. Transform (x GT_GT.unsigned 0) into (x GT_NE 0)
92089212
// and (x GT_LE.unsigned 0) into (x GT_EQ 0). The later case is rare, it sometimes
92099213
// occurs as a result of branch inversion.
9214+
// Non-neg case:
9215+
// ("expr > 0") equivalent to ("expr != 0")
9216+
// ("expr <= 0") equivalent to ("expr == 0")
92109217
oper = (oper == GT_LE) ? GT_EQ : GT_NE;
92119218
cmp->gtFlags &= ~GTF_UNSIGNED;
92129219
}
92139220
// LE_UN/GT_UN(expr, int/long.MaxValue) => GE/LT(expr, 0).
9221+
// LE/GT(non-negative expr, int/long.MaxValue) => GE/LT(expr, 0)
92149222
else if (((op1->TypeIs(TYP_LONG) && (op2Value == INT64_MAX))) ||
92159223
((genActualType(op1) == TYP_INT) && (op2Value == INT32_MAX)))
92169224
{
92179225
oper = (oper == GT_LE) ? GT_GE : GT_LT;
92189226
cmp->gtFlags &= ~GTF_UNSIGNED;
92199227
}
9220-
// LE_UN/GT_UN(expr, int.MaxValue) => EQ/NE(RSZ(expr, 32), 0).
9228+
// LE_UN/GT_UN(expr, uint.MaxValue) => EQ/NE(RSZ(expr, 32), 0).
9229+
// LE/GT(non-negative expr, uint.MaxValue) => EQ/NE(RSZ(expr, 32), 0).
92219230
else if (opts.OptimizationEnabled() && (op1->TypeIs(TYP_LONG) && (op2Value == UINT_MAX)))
92229231
{
92239232
oper = (oper == GT_GT) ? GT_NE : GT_EQ;

0 commit comments

Comments
 (0)