From f9bdee6e5266794f237368496288300c3b47aa22 Mon Sep 17 00:00:00 2001 From: varelen Date: Wed, 29 Jan 2025 21:11:57 +0100 Subject: [PATCH 1/4] JIT: Optimize bit-wise AND with a constant mask in combination with a left shift in a compare This optmizes the generated code when having a pattern like '(SomeConstant & (1 << value)) != 0' which was previously only optimized for '(variable & (1 << value)) != 0'. Fix #111554 --- src/coreclr/jit/lower.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 484bf1454e5fd3..1b4c568a7b2980 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4306,16 +4306,29 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // operand bit size - it uses (bit_index MOD bit_size). // - GenTree* lsh = cmp->gtGetOp2(); + GenTree* lshRight = cmp->gtGetOp2(); + GenTree* lshLeft = cmp->gtGetOp1(); - if (lsh->OperIs(GT_LSH) && varTypeIsIntOrI(lsh->TypeGet()) && lsh->gtGetOp1()->IsIntegralConst(1)) + if (lshRight->OperIs(GT_LSH) && varTypeIsIntOrI(lshRight->TypeGet()) && lshRight->gtGetOp1()->IsIntegralConst(1)) { cmp->SetOper(cmp->OperIs(GT_TEST_EQ) ? GT_BITTEST_EQ : GT_BITTEST_NE); - cmp->AsOp()->gtOp2 = lsh->gtGetOp2(); + cmp->AsOp()->gtOp2 = lshRight->gtGetOp2(); cmp->gtGetOp2()->ClearContained(); - BlockRange().Remove(lsh->gtGetOp1()); - BlockRange().Remove(lsh); + BlockRange().Remove(lshRight->gtGetOp1()); + BlockRange().Remove(lshRight); + + return cmp->gtNext; + } + else if (lshLeft->OperIs(GT_LSH) && varTypeIsIntOrI(lshLeft->TypeGet()) && lshLeft->gtGetOp1()->IsIntegralConst(1)) + { + cmp->SetOper(cmp->OperIs(GT_TEST_EQ) ? GT_BITTEST_EQ : GT_BITTEST_NE); + cmp->AsOp()->gtOp1 = cmp->AsOp()->gtOp2; + cmp->AsOp()->gtOp2 = lshLeft->gtGetOp2(); + cmp->gtGetOp2()->ClearContained(); + + BlockRange().Remove(lshLeft->gtGetOp1()); + BlockRange().Remove(lshLeft); return cmp->gtNext; } From 31c3ac357ee68bfa2350e58a6836077906be677c Mon Sep 17 00:00:00 2001 From: varelen Date: Fri, 31 Jan 2025 20:58:00 +0100 Subject: [PATCH 2/4] JIT: Minimize code duplicate when lowering left shift with const --- src/coreclr/jit/lower.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 1b4c568a7b2980..7138e32f1ba87b 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4306,29 +4306,31 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // operand bit size - it uses (bit_index MOD bit_size). // - GenTree* lshRight = cmp->gtGetOp2(); - GenTree* lshLeft = cmp->gtGetOp1(); + GenTree** lsh = &cmp->AsOp()->gtOp1; + GenTree** op = &cmp->AsOp()->gtOp2; + bool swapCompareOps = true; - if (lshRight->OperIs(GT_LSH) && varTypeIsIntOrI(lshRight->TypeGet()) && lshRight->gtGetOp1()->IsIntegralConst(1)) + if (!(*lsh)->OperIs(GT_LSH)) { - cmp->SetOper(cmp->OperIs(GT_TEST_EQ) ? GT_BITTEST_EQ : GT_BITTEST_NE); - cmp->AsOp()->gtOp2 = lshRight->gtGetOp2(); - cmp->gtGetOp2()->ClearContained(); - - BlockRange().Remove(lshRight->gtGetOp1()); - BlockRange().Remove(lshRight); - - return cmp->gtNext; + std::swap(lsh, op); + swapCompareOps = false; } - else if (lshLeft->OperIs(GT_LSH) && varTypeIsIntOrI(lshLeft->TypeGet()) && lshLeft->gtGetOp1()->IsIntegralConst(1)) + + if ((*lsh)->OperIs(GT_LSH) && varTypeIsIntOrI(*lsh) && (*lsh)->gtGetOp1()->IsIntegralConst(1)) { cmp->SetOper(cmp->OperIs(GT_TEST_EQ) ? GT_BITTEST_EQ : GT_BITTEST_NE); - cmp->AsOp()->gtOp1 = cmp->AsOp()->gtOp2; - cmp->AsOp()->gtOp2 = lshLeft->gtGetOp2(); - cmp->gtGetOp2()->ClearContained(); - BlockRange().Remove(lshLeft->gtGetOp1()); - BlockRange().Remove(lshLeft); + BlockRange().Remove((*lsh)->gtGetOp1()); + BlockRange().Remove(*lsh); + + *lsh = (*lsh)->gtGetOp2(); + + if (swapCompareOps) + { + std::swap(cmp->AsOp()->gtOp1, cmp->AsOp()->gtOp2); + } + + cmp->gtGetOp2()->ClearContained(); return cmp->gtNext; } From f1d8d607fcc4b0d386474f3442602d1886485832 Mon Sep 17 00:00:00 2001 From: varelen Date: Fri, 31 Jan 2025 21:05:03 +0100 Subject: [PATCH 3/4] JIT: Include test/eq with operands switched containing left shift in comment --- src/coreclr/jit/lower.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 7138e32f1ba87b..f3a139f20066c2 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4299,7 +4299,7 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (cmp->OperIs(GT_TEST_EQ, GT_TEST_NE)) { // - // Transform TEST_EQ|NE(x, LSH(1, y)) into BT(x, y) when possible. Using BT + // Transform TEST_EQ|NE(x, LSH(1, y)) or TEST_EQ|NE(LSH(1, y), x) into BT(x, y) when possible. Using BT // results in smaller and faster code. It also doesn't have special register // requirements, unlike LSH that requires the shift count to be in ECX. // Note that BT has the same behavior as LSH when the bit index exceeds the From 5783c3c465b7bf85381db354b901b5d67bff7c07 Mon Sep 17 00:00:00 2001 From: varelen Date: Fri, 31 Jan 2025 22:30:43 +0100 Subject: [PATCH 4/4] JIT: Improve code for optimizing test/eq with a left shift op --- src/coreclr/jit/lower.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index f3a139f20066c2..e4e30e43003acb 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4306,30 +4306,23 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // operand bit size - it uses (bit_index MOD bit_size). // - GenTree** lsh = &cmp->AsOp()->gtOp1; - GenTree** op = &cmp->AsOp()->gtOp2; - bool swapCompareOps = true; + GenTree* lsh = cmp->AsOp()->gtOp1; + GenTree* op = cmp->AsOp()->gtOp2; - if (!(*lsh)->OperIs(GT_LSH)) + if (!lsh->OperIs(GT_LSH)) { std::swap(lsh, op); - swapCompareOps = false; } - if ((*lsh)->OperIs(GT_LSH) && varTypeIsIntOrI(*lsh) && (*lsh)->gtGetOp1()->IsIntegralConst(1)) + if (lsh->OperIs(GT_LSH) && varTypeIsIntOrI(lsh) && lsh->gtGetOp1()->IsIntegralConst(1)) { cmp->SetOper(cmp->OperIs(GT_TEST_EQ) ? GT_BITTEST_EQ : GT_BITTEST_NE); - BlockRange().Remove((*lsh)->gtGetOp1()); - BlockRange().Remove(*lsh); - - *lsh = (*lsh)->gtGetOp2(); - - if (swapCompareOps) - { - std::swap(cmp->AsOp()->gtOp1, cmp->AsOp()->gtOp2); - } + BlockRange().Remove(lsh->gtGetOp1()); + BlockRange().Remove(lsh); + cmp->AsOp()->gtOp1 = op; + cmp->AsOp()->gtOp2 = lsh->gtGetOp2(); cmp->gtGetOp2()->ClearContained(); return cmp->gtNext;