From b8e5b03358999e390b8b27d9c52a2d2327c38f93 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 16:04:14 +0100 Subject: [PATCH 01/22] Simplify arithmetic operations on registry and memory --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/morph.cpp | 37 ++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index cb1137a8b4d0c5..0d13a7a8154296 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6690,7 +6690,7 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); - GenTree* fgMorphReduceAddOps(GenTree* tree); + GenTree* fgMorphReduceAddOrSubOps(GenTree* tree); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0531aef7d53e6c..273c6dca17f8b4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7518,7 +7518,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (opts.OptimizationEnabled() && fgGlobalMorph) { - GenTree* morphed = fgMorphReduceAddOps(tree); + GenTree* morphed = fgMorphReduceAddOrSubOps(tree); if (morphed != tree) return fgMorphTree(morphed); } @@ -15442,8 +15442,9 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) } //------------------------------------------------------------------------ -// fgMorphReduceAddOps: reduce successive variable adds into a single multiply, +// fgMorphReduceAddOps: reduce successive variable adds/subs into a single multiply, // e.g., i + i + i + i => i * 4. +// e.g., i - i - i - i => - i * 2. // // Arguments: // tree - tree for reduction @@ -15451,17 +15452,18 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) // Return Value: // reduced tree if pattern matches, original tree otherwise // -GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) +GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) { // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) + if (!tree->OperIs(GT_ADD, GT_SUB) || tree->gtOverflow()) { return tree; } + genTreeOps targetOp = tree->OperGet(); #if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing - // ADD ops with a helper function call. Don't apply optimization in that case. + // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing + // ADD/SUB ops with a helper function call. Don't apply optimization in that case. if (tree->TypeIs(TYP_LONG)) { return tree; @@ -15482,21 +15484,33 @@ GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) int foldCount = 0; unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); - // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum). + // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum) OR SUB(SUB(SUB(lclNum, lclNum), + // lclNum), lclNum). while (true) { // ADD(lclNum, lclNum), end of tree if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { - foldCount += 2; + if (targetOp == GT_ADD) + { + foldCount += 2; + } break; } - // ADD(ADD(X, Y), lclNum), keep descending - else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && + // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending + else if (op1->OperIs(targetOp) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { - foldCount++; + if (targetOp == GT_ADD) + { + foldCount++; + } + else + { + foldCount--; + } + op2 = op1->AsOp()->gtOp2; op1 = op1->AsOp()->gtOp1; } @@ -15508,6 +15522,7 @@ GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) } // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize + // V0 - V0 ... - V0 becomes V0 * (- foldCount + 1), where postorder transform will optimize // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); From 4507495cb3b8d5ee4d6870252e141c8c55977223 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 16:20:19 +0100 Subject: [PATCH 02/22] chain two simplifications --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/morph.cpp | 105 +++++++++++++++++++++++++++++-------- 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 0d13a7a8154296..e20802333310e6 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6690,7 +6690,8 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); - GenTree* fgMorphReduceAddOrSubOps(GenTree* tree); + GenTree* fgMorphReduceAddOps(GenTree* tree); + GenTree* fgMorphReduceSubOps(GenTree* tree); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 273c6dca17f8b4..84ef1dcfce1752 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7518,7 +7518,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (opts.OptimizationEnabled() && fgGlobalMorph) { - GenTree* morphed = fgMorphReduceAddOrSubOps(tree); + GenTree* morphed = fgMorphReduceSubOps(fgMorphReduceAddOps(tree)); if (morphed != tree) return fgMorphTree(morphed); } @@ -15442,7 +15442,7 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) } //------------------------------------------------------------------------ -// fgMorphReduceAddOps: reduce successive variable adds/subs into a single multiply, +// fgMorphReduceAddOps: reduce successive variable adds into a single multiply, // e.g., i + i + i + i => i * 4. // e.g., i - i - i - i => - i * 2. // @@ -15452,18 +15452,17 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) // Return Value: // reduced tree if pattern matches, original tree otherwise // -GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) +GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) { // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_ADD, GT_SUB) || tree->gtOverflow()) + if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) { return tree; } - genTreeOps targetOp = tree->OperGet(); #if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing - // ADD/SUB ops with a helper function call. Don't apply optimization in that case. + // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing + // ADD ops with a helper function call. Don't apply optimization in that case. if (tree->TypeIs(TYP_LONG)) { return tree; @@ -15492,25 +15491,14 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { - if (targetOp == GT_ADD) - { - foldCount += 2; - } + foldCount += 2; break; } // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending - else if (op1->OperIs(targetOp) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && + else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { - if (targetOp == GT_ADD) - { - foldCount++; - } - else - { - foldCount--; - } - + foldCount++; op2 = op1->AsOp()->gtOp2; op1 = op1->AsOp()->gtOp1; } @@ -15522,6 +15510,81 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) } // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize + // accordingly + consTree->BashToConst(foldCount, tree->TypeGet()); + + GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); + DEBUG_DESTROY_NODE(tree); + + return morphed; +} + +//------------------------------------------------------------------------ +// fgMorphReduceSubOps: reduce successive variable subs into a single multiply, +// e.g., i - i - i - i => - i * 2. +// +// Arguments: +// tree - tree for reduction +// +// Return Value: +// reduced tree if pattern matches, original tree otherwise +// +GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) +{ + // SUB(_, V0) starts the pattern match. + if (!tree->OperIs(GT_SUB) || tree->gtOverflow()) + { + return tree; + } + + genTreeOps targetOp = tree->OperGet(); +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit SUB to 64-bit MUL on 32-bit system results in replacing + // SUB ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return tree; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + + GenTree* lclVarTree = tree->AsOp()->gtOp2; + GenTree* consTree = tree->AsOp()->gtOp1; + + GenTree* op1 = consTree; + GenTree* op2 = lclVarTree; + + if (!op2->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) + { + return tree; + } + + int foldCount = 0; + unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); + + // Search for pattern of shape SUB(SUB(SUB(lclNum, lclNum), lclNum), lclNum). + while (true) + { + // ADD(lclNum, lclNum), end of tree + if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && + op2->AsLclVarCommon()->GetLclNum() == lclNum) + { + break; + } + // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending + else if (op1->OperIs(targetOp) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && + op2->AsLclVarCommon()->GetLclNum() == lclNum) + { + foldCount--; + op2 = op1->AsOp()->gtOp2; + op1 = op1->AsOp()->gtOp1; + } + // Any other case is a pattern we won't attempt to fold for now. + else + { + return tree; + } + } + // V0 - V0 ... - V0 becomes V0 * (- foldCount + 1), where postorder transform will optimize // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); From e30f77750e26354a174b15a6100a37765a75723e Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 16:21:54 +0100 Subject: [PATCH 03/22] eliminate some diffs --- src/coreclr/jit/morph.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 84ef1dcfce1752..6d42cc4f14a05b 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15483,8 +15483,7 @@ GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) int foldCount = 0; unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); - // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum) OR SUB(SUB(SUB(lclNum, lclNum), - // lclNum), lclNum). + // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum). while (true) { // ADD(lclNum, lclNum), end of tree @@ -15494,7 +15493,7 @@ GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) foldCount += 2; break; } - // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending + // ADD(ADD(X, Y), lclNum), keep descending else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { @@ -15564,13 +15563,13 @@ GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) // Search for pattern of shape SUB(SUB(SUB(lclNum, lclNum), lclNum), lclNum). while (true) { - // ADD(lclNum, lclNum), end of tree + // SUB(lclNum, lclNum), end of tree if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { break; } - // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending + // SUB(SUB(X, Y), lclNum), keep descending else if (op1->OperIs(targetOp) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) { From af7e04121553a5927916812a0b466b9f765cb6f2 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 16:22:25 +0100 Subject: [PATCH 04/22] fix diffs --- src/coreclr/jit/morph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 6d42cc4f14a05b..ceb7be3b9ac18f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15444,7 +15444,6 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) //------------------------------------------------------------------------ // fgMorphReduceAddOps: reduce successive variable adds into a single multiply, // e.g., i + i + i + i => i * 4. -// e.g., i - i - i - i => - i * 2. // // Arguments: // tree - tree for reduction From f873cab9d977522c27cf6e9ce2a3c948d5b48ec9 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 17:23:33 +0100 Subject: [PATCH 05/22] add other simplifications --- src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/morph.cpp | 162 ++++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e20802333310e6..1055a4d5ace68e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6691,7 +6691,9 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); GenTree* fgMorphReduceAddOps(GenTree* tree); + GenTree* fgMorphReduceIndirAddOps(GenTree* tree); GenTree* fgMorphReduceSubOps(GenTree* tree); + GenTree* fgMorphReduceIndirSubOps(GenTree* tree); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ceb7be3b9ac18f..0784f80f2e1dc8 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7518,7 +7518,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (opts.OptimizationEnabled() && fgGlobalMorph) { - GenTree* morphed = fgMorphReduceSubOps(fgMorphReduceAddOps(tree)); + GenTree* morphed = + fgMorphReduceIndirSubOps(fgMorphReduceSubOps(fgMorphReduceIndirAddOps(fgMorphReduceAddOps(tree)))); if (morphed != tree) return fgMorphTree(morphed); } @@ -15517,6 +15518,85 @@ GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) return morphed; } +//------------------------------------------------------------------------ +// fgMorphReduceIndirAddOps: reduce successive variable adds into a single multiply, +// e.g., i + i + i + i => i * 4. +// +// Arguments: +// tree - tree for reduction +// +// Return Value: +// reduced tree if pattern matches, original tree otherwise +// +GenTree* Compiler::fgMorphReduceIndirAddOps(GenTree* tree) +{ + // ADD(_, V0) starts the pattern match. + if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) + { + return tree; + } + +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing + // ADD ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return tree; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + + GenTree* lclVarTree = tree->AsOp()->gtOp2; + GenTree* consTree = tree->AsOp()->gtOp1; + + GenTree* op1 = consTree; + GenTree* op2 = lclVarTree; + + if (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) + { + return tree; + } + + int foldCount = 0; + unsigned lclNum = op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); + + // Search for pattern of shape ADD(ADD(ADD(IND(lclNum), IND(lclNum)), IND(lclNum)), IND(lclNum)). + while (true) + { + // ADD(IND(lclNum), IND(lclNum)), end of tree + if (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && + op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) + { + foldCount += 2; + break; + } + // ADD(ADD(X, Y), IND(lclNum)), keep descending + else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_IND) && + op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) + { + foldCount++; + op2 = op1->AsOp()->gtOp2; + op1 = op1->AsOp()->gtOp1; + } + // Any other case is a pattern we won't attempt to fold for now. + else + { + return tree; + } + } + + // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize + // accordingly + consTree->BashToConst(foldCount, tree->TypeGet()); + + GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); + DEBUG_DESTROY_NODE(tree); + + return morphed; +} + //------------------------------------------------------------------------ // fgMorphReduceSubOps: reduce successive variable subs into a single multiply, // e.g., i - i - i - i => - i * 2. @@ -15583,7 +15663,85 @@ GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) } } - // V0 - V0 ... - V0 becomes V0 * (- foldCount + 1), where postorder transform will optimize + // V0 - V0 ... - V0 becomes V0 * (- foldCount + 2), where postorder transform will optimize + // accordingly + consTree->BashToConst(foldCount, tree->TypeGet()); + + GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); + DEBUG_DESTROY_NODE(tree); + + return morphed; +} + +//------------------------------------------------------------------------ +// fgMorphReduceIndirAddOps: reduce successive variable adds into a single multiply, +// e.g., i - i - i - i => - i * 2. +// +// Arguments: +// tree - tree for reduction +// +// Return Value: +// reduced tree if pattern matches, original tree otherwise +// +GenTree* Compiler::fgMorphReduceIndirSubOps(GenTree* tree) +{ + // ADD(_, V0) starts the pattern match. + if (!tree->OperIs(GT_SUB) || tree->gtOverflow()) + { + return tree; + } + +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit SUB to 64-bit MUL on 32-bit system results in replacing + // SUB ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return tree; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + + GenTree* lclVarTree = tree->AsOp()->gtOp2; + GenTree* consTree = tree->AsOp()->gtOp1; + + GenTree* op1 = consTree; + GenTree* op2 = lclVarTree; + + if (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) + { + return tree; + } + + int foldCount = 0; + unsigned lclNum = op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); + + // Search for pattern of shape SUB(SUB(SUB(IND(lclNum), IND(lclNum)), IND(lclNum)), IND(lclNum)). + while (true) + { + // SUB(IND(lclNum), IND(lclNum)), end of tree + if (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && + op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) + { + break; + } + // SUB(SUB(X, Y), IND(lclNum)), keep descending + else if (op1->OperIs(GT_SUB) && !op1->gtOverflow() && op2->OperIs(GT_IND) && + op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) + { + foldCount--; + op2 = op1->AsOp()->gtOp2; + op1 = op1->AsOp()->gtOp1; + } + // Any other case is a pattern we won't attempt to fold for now. + else + { + return tree; + } + } + + // V0 - V0 ... - V0 becomes V0 * -foldCount + 2, where postorder transform will optimize // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); From d35864c1279df50389a1938db80344f7c95c88aa Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 20:22:23 +0100 Subject: [PATCH 06/22] reduce LOC --- src/coreclr/jit/compiler.h | 5 +- src/coreclr/jit/morph.cpp | 288 +++++-------------------------------- 2 files changed, 39 insertions(+), 254 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1055a4d5ace68e..0d13a7a8154296 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6690,10 +6690,7 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); - GenTree* fgMorphReduceAddOps(GenTree* tree); - GenTree* fgMorphReduceIndirAddOps(GenTree* tree); - GenTree* fgMorphReduceSubOps(GenTree* tree); - GenTree* fgMorphReduceIndirSubOps(GenTree* tree); + GenTree* fgMorphReduceAddOrSubOps(GenTree* tree); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0784f80f2e1dc8..5e12b07b17fcb6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7518,8 +7518,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (opts.OptimizationEnabled() && fgGlobalMorph) { - GenTree* morphed = - fgMorphReduceIndirSubOps(fgMorphReduceSubOps(fgMorphReduceIndirAddOps(fgMorphReduceAddOps(tree)))); + GenTree* morphed = fgMorphReduceAddOrSubOps(tree); if (morphed != tree) return fgMorphTree(morphed); } @@ -15443,162 +15442,8 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) } //------------------------------------------------------------------------ -// fgMorphReduceAddOps: reduce successive variable adds into a single multiply, +// fgMorphReduceAddOrSubOps: reduce successive variable adds/subs into a single multiply, // e.g., i + i + i + i => i * 4. -// -// Arguments: -// tree - tree for reduction -// -// Return Value: -// reduced tree if pattern matches, original tree otherwise -// -GenTree* Compiler::fgMorphReduceAddOps(GenTree* tree) -{ - // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) - { - return tree; - } - -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing - // ADD ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return tree; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - - GenTree* lclVarTree = tree->AsOp()->gtOp2; - GenTree* consTree = tree->AsOp()->gtOp1; - - GenTree* op1 = consTree; - GenTree* op2 = lclVarTree; - - if (!op2->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) - { - return tree; - } - - int foldCount = 0; - unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); - - // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum). - while (true) - { - // ADD(lclNum, lclNum), end of tree - if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount += 2; - break; - } - // ADD(ADD(X, Y), lclNum), keep descending - else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount++; - op2 = op1->AsOp()->gtOp2; - op1 = op1->AsOp()->gtOp1; - } - // Any other case is a pattern we won't attempt to fold for now. - else - { - return tree; - } - } - - // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize - // accordingly - consTree->BashToConst(foldCount, tree->TypeGet()); - - GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); - DEBUG_DESTROY_NODE(tree); - - return morphed; -} - -//------------------------------------------------------------------------ -// fgMorphReduceIndirAddOps: reduce successive variable adds into a single multiply, -// e.g., i + i + i + i => i * 4. -// -// Arguments: -// tree - tree for reduction -// -// Return Value: -// reduced tree if pattern matches, original tree otherwise -// -GenTree* Compiler::fgMorphReduceIndirAddOps(GenTree* tree) -{ - // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_ADD) || tree->gtOverflow()) - { - return tree; - } - -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD to 64-bit MUL on 32-bit system results in replacing - // ADD ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return tree; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - - GenTree* lclVarTree = tree->AsOp()->gtOp2; - GenTree* consTree = tree->AsOp()->gtOp1; - - GenTree* op1 = consTree; - GenTree* op2 = lclVarTree; - - if (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) - { - return tree; - } - - int foldCount = 0; - unsigned lclNum = op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); - - // Search for pattern of shape ADD(ADD(ADD(IND(lclNum), IND(lclNum)), IND(lclNum)), IND(lclNum)). - while (true) - { - // ADD(IND(lclNum), IND(lclNum)), end of tree - if (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && - op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount += 2; - break; - } - // ADD(ADD(X, Y), IND(lclNum)), keep descending - else if (op1->OperIs(GT_ADD) && !op1->gtOverflow() && op2->OperIs(GT_IND) && - op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount++; - op2 = op1->AsOp()->gtOp2; - op1 = op1->AsOp()->gtOp1; - } - // Any other case is a pattern we won't attempt to fold for now. - else - { - return tree; - } - } - - // V0 + V0 ... + V0 becomes V0 * foldCount, where postorder transform will optimize - // accordingly - consTree->BashToConst(foldCount, tree->TypeGet()); - - GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); - DEBUG_DESTROY_NODE(tree); - - return morphed; -} - -//------------------------------------------------------------------------ -// fgMorphReduceSubOps: reduce successive variable subs into a single multiply, // e.g., i - i - i - i => - i * 2. // // Arguments: @@ -15607,18 +15452,18 @@ GenTree* Compiler::fgMorphReduceIndirAddOps(GenTree* tree) // Return Value: // reduced tree if pattern matches, original tree otherwise // -GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) +GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) { - // SUB(_, V0) starts the pattern match. - if (!tree->OperIs(GT_SUB) || tree->gtOverflow()) + // ADD(_, V0) starts the pattern match. + if (!tree->OperIs(GT_ADD, GT_SUB) || tree->gtOverflow()) { return tree; } genTreeOps targetOp = tree->OperGet(); #if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit SUB to 64-bit MUL on 32-bit system results in replacing - // SUB ops with a helper function call. Don't apply optimization in that case. + // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing + // ADD/SUB ops with a helper function call. Don't apply optimization in that case. if (tree->TypeIs(TYP_LONG)) { return tree; @@ -15631,28 +15476,48 @@ GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) GenTree* op1 = consTree; GenTree* op2 = lclVarTree; - if (!op2->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) + if ((!op2->OperIs(GT_LCL_VAR) && (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR))) || + !varTypeIsIntegral(op2)) { return tree; } int foldCount = 0; - unsigned lclNum = op2->AsLclVarCommon()->GetLclNum(); + unsigned lclNum = op2->OperIs(GT_LCL_VAR) ? op2->AsLclVarCommon()->GetLclNum() + : op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); - // Search for pattern of shape SUB(SUB(SUB(lclNum, lclNum), lclNum), lclNum). + // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum) OR SUB(SUB(SUB(lclNum, lclNum), + // lclNum), lclNum). while (true) { - // SUB(lclNum, lclNum), end of tree - if (op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) + // ADD(lclNum, lclNum), end of tree + if ((op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && + op2->AsLclVarCommon()->GetLclNum() == lclNum) || + (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && + op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum)) { + if (targetOp == GT_ADD) + { + foldCount += 2; + } break; } - // SUB(SUB(X, Y), lclNum), keep descending - else if (op1->OperIs(targetOp) && !op1->gtOverflow() && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) + // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending + else if (op1->OperIs(targetOp) && !op1->gtOverflow() && + ((op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) || + (op2->OperIs(GT_IND) && op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && + op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum))) { - foldCount--; + if (targetOp == GT_ADD) + { + foldCount++; + } + else + { + foldCount--; + } op2 = op1->AsOp()->gtOp2; op1 = op1->AsOp()->gtOp1; } @@ -15663,6 +15528,7 @@ GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) } } + // V0 + V0 ... + V0 becomes V0 * foldCount, // V0 - V0 ... - V0 becomes V0 * (- foldCount + 2), where postorder transform will optimize // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); @@ -15673,84 +15539,6 @@ GenTree* Compiler::fgMorphReduceSubOps(GenTree* tree) return morphed; } -//------------------------------------------------------------------------ -// fgMorphReduceIndirAddOps: reduce successive variable adds into a single multiply, -// e.g., i - i - i - i => - i * 2. -// -// Arguments: -// tree - tree for reduction -// -// Return Value: -// reduced tree if pattern matches, original tree otherwise -// -GenTree* Compiler::fgMorphReduceIndirSubOps(GenTree* tree) -{ - // ADD(_, V0) starts the pattern match. - if (!tree->OperIs(GT_SUB) || tree->gtOverflow()) - { - return tree; - } - -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit SUB to 64-bit MUL on 32-bit system results in replacing - // SUB ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return tree; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - - GenTree* lclVarTree = tree->AsOp()->gtOp2; - GenTree* consTree = tree->AsOp()->gtOp1; - - GenTree* op1 = consTree; - GenTree* op2 = lclVarTree; - - if (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) || !varTypeIsIntegral(op2)) - { - return tree; - } - - int foldCount = 0; - unsigned lclNum = op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); - - // Search for pattern of shape SUB(SUB(SUB(IND(lclNum), IND(lclNum)), IND(lclNum)), IND(lclNum)). - while (true) - { - // SUB(IND(lclNum), IND(lclNum)), end of tree - if (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && - op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) - { - break; - } - // SUB(SUB(X, Y), IND(lclNum)), keep descending - else if (op1->OperIs(GT_SUB) && !op1->gtOverflow() && op2->OperIs(GT_IND) && - op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum) - { - foldCount--; - op2 = op1->AsOp()->gtOp2; - op1 = op1->AsOp()->gtOp1; - } - // Any other case is a pattern we won't attempt to fold for now. - else - { - return tree; - } - } - - // V0 - V0 ... - V0 becomes V0 * -foldCount + 2, where postorder transform will optimize - // accordingly - consTree->BashToConst(foldCount, tree->TypeGet()); - - GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); - DEBUG_DESTROY_NODE(tree); - - return morphed; -} - //------------------------------------------------------------------------ // Compiler::MorphMDArrayTempCache::TempList::GetTemp: return a local variable number to use as a temporary variable // in multi-dimensional array operation expansion. From af6a71e15d7fdbb113eade7e4009d7dd337a55f8 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Sat, 14 Mar 2026 23:50:42 +0100 Subject: [PATCH 07/22] fix comment --- src/coreclr/jit/morph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 5e12b07b17fcb6..c8b6fb5cccd96f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15454,7 +15454,7 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) // GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) { - // ADD(_, V0) starts the pattern match. + // ADD(_, V0) OR SUB(_, V0) starts the pattern match. if (!tree->OperIs(GT_ADD, GT_SUB) || tree->gtOverflow()) { return tree; @@ -15490,7 +15490,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) // lclNum), lclNum). while (true) { - // ADD(lclNum, lclNum), end of tree + // ADD(lclNum, lclNum) OR SUB(lclNum, lclNum), end of tree if ((op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) || (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && From 26e899d3f3c22ad8ee6e5fd72be5cebb97013404 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Tue, 7 Apr 2026 16:41:05 +0200 Subject: [PATCH 08/22] handle more cases --- src/coreclr/jit/gentree.h | 65 +++++++++++++++++++++++++++++++++++++++ src/coreclr/jit/morph.cpp | 53 ++++++++++++++++--------------- 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 178cfd3c48e864..0a13c1bd681a95 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2013,6 +2013,8 @@ struct GenTree // Sets the GTF flag equivalent for the regIndex'th register of a multi-reg node. void SetRegSpillFlagByIdx(GenTreeFlags flags, int regIndex); + bool OperIsIntScalarMulLclVar(ssize_t* scalar, unsigned int* lclNum); + #ifdef TARGET_ARM64 bool NeedsConsecutiveRegisters() const; #endif @@ -10582,6 +10584,69 @@ inline GenTree* GenTree::GetIndirOrArrMetaDataAddr() } } +inline bool GenTree::OperIsIntScalarMulLclVar(ssize_t* scalar, unsigned int* lclNum) +{ + if (OperIs(GT_LCL_VAR)) + { + *scalar = 1; + *lclNum = AsLclVar()->GetLclNum(); + return true; + } + else if (OperIs(GT_IND) && AsIndir()->Base()->OperIs(GT_LCL_VAR)) + { + *scalar = 1; + *lclNum = AsIndir()->Base()->AsLclVar()->GetLclNum(); + return true; + } + + GenTree* op = this; + bool reverseSign = false; + if (OperIs(GT_NEG)) + { + op = op->gtGetOp1(); + reverseSign = true; + } + + if (!op->OperIs(GT_MUL)) + { + return false; + } + + GenTree* op1 = op->gtGetOp1(); + GenTree* op2 = op->gtGetOp2(); + + if (op1->IsIntegralConst() && op2->OperIs(GT_LCL_VAR)) + { + *scalar = op1->AsIntConCommon()->IconValue(); + *lclNum = op2->AsLclVar()->GetLclNum(); + } + else if (op2->IsIntegralConst() && op1->OperIs(GT_LCL_VAR)) + { + *scalar = op2->AsIntConCommon()->IconValue(); + *lclNum = op1->AsLclVar()->GetLclNum(); + } + else if (op1->IsIntegralConst() && op2->OperIs(GT_IND) && op2->AsIndir()->Base()->OperIs(GT_LCL_VAR)) + { + *scalar = op1->AsIntConCommon()->IconValue(); + *lclNum = op2->AsIndir()->Base()->AsLclVar()->GetLclNum(); + } + else if (op2->IsIntegralConst() && op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR)) + { + *scalar = op2->AsIntConCommon()->IconValue(); + *lclNum = op1->AsIndir()->Base()->AsLclVar()->GetLclNum(); + } + else + { + return false; + } + + if (reverseSign) + { + *scalar = -(*scalar); + } + return true; +} + /*****************************************************************************/ #ifndef HOST_64BIT diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index fd04da97adf5bc..c021c56708ae3f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15477,8 +15477,7 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) //------------------------------------------------------------------------ // fgMorphReduceAddOrSubOps: reduce successive variable adds/subs into a single multiply, -// e.g., i + i + i + i => i * 4. -// e.g., i - i - i - i => - i * 2. +// e.g., i + 3 * i + i - 4 * i + i => i * 2. // // Arguments: // tree - tree for reduction @@ -15507,53 +15506,53 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) GenTree* lclVarTree = tree->AsOp()->gtOp2; GenTree* consTree = tree->AsOp()->gtOp1; - GenTree* op1 = consTree; - GenTree* op2 = lclVarTree; + GenTree* op1 = consTree; + ssize_t op1Scalar = 0; + unsigned int op1lclNum = 0; + GenTree* op2 = lclVarTree; + ssize_t op2Scalar = 0; + unsigned int op2lclNum = 0; - if ((!op2->OperIs(GT_LCL_VAR) && (!op2->OperIs(GT_IND) || !op2->AsIndir()->Base()->OperIs(GT_LCL_VAR))) || - !varTypeIsIntegral(op2)) + if (!op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) || !varTypeIsIntegral(op2)) { return tree; } - int foldCount = 0; - unsigned lclNum = op2->OperIs(GT_LCL_VAR) ? op2->AsLclVarCommon()->GetLclNum() - : op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum(); + ssize_t foldCount = 0; + unsigned int lclNum = op2lclNum; - // Search for pattern of shape ADD(ADD(ADD(lclNum, lclNum), lclNum), lclNum) OR SUB(SUB(SUB(lclNum, lclNum), - // lclNum), lclNum). + // Search for pattern of shape ADD(SUB(ADD(lclNum, lclNum), lclNum), lclNum). while (true) { // ADD(lclNum, lclNum) OR SUB(lclNum, lclNum), end of tree - if ((op1->OperIs(GT_LCL_VAR) && op1->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_LCL_VAR) && - op2->AsLclVarCommon()->GetLclNum() == lclNum) || - (op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op1->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum && op2->OperIs(GT_IND) && - op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum)) + if ((op1->OperIsIntScalarMulLclVar(&op1Scalar, &op1lclNum) && op1lclNum == lclNum) && + (op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) && op2lclNum == lclNum)) { if (targetOp == GT_ADD) { - foldCount += 2; + foldCount += op1Scalar + op2Scalar; + } + else + { + foldCount += op1Scalar - op2Scalar; } break; } - // ADD(ADD(X, Y), lclNum) OR SUB(SUB(X, Y), lclNum), keep descending - else if (op1->OperIs(targetOp) && !op1->gtOverflow() && - ((op2->OperIs(GT_LCL_VAR) && op2->AsLclVarCommon()->GetLclNum() == lclNum) || - (op2->OperIs(GT_IND) && op2->AsIndir()->Base()->OperIs(GT_LCL_VAR) && - op2->AsIndir()->Base()->AsLclVarCommon()->GetLclNum() == lclNum))) + // ADD(SUB(X, Y), lclNum), keep descending + else if (op1->OperIs(GT_ADD, GT_SUB) && !op1->gtOverflow() && + (op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) && op2lclNum == lclNum)) { if (targetOp == GT_ADD) { - foldCount++; + foldCount += op2Scalar; } else { - foldCount--; + foldCount -= op2Scalar; } - op2 = op1->AsOp()->gtOp2; - op1 = op1->AsOp()->gtOp1; + targetOp = op1->OperGet(); + op2 = op1->AsOp()->gtOp2; + op1 = op1->AsOp()->gtOp1; } // Any other case is a pattern we won't attempt to fold for now. else From 3a6e8c3a7f3f6f48dbd57e8cbe61b5c67cbb9db3 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Wed, 8 Apr 2026 18:14:53 +0200 Subject: [PATCH 09/22] try calling morph from ssa --- src/coreclr/jit/assertionprop.cpp | 39 ++++++++++++++++++++++++++++++- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/morph.cpp | 8 +++---- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 9c5347eb7af9f5..e01dbd92811b6a 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2655,6 +2655,41 @@ GenTree* Compiler::optVNBasedFoldExpr_Call(BasicBlock* block, GenTree* parent, G return nullptr; } +//------------------------------------------------------------------------------ +// optVNBasedFoldExpr_Call: Folds given ADD/SUB operation using VN to a simpler MUL tree. +// +// Arguments: +// block - The block containing the tree. +// parent - The parent node of the tree. +// tree - The ADD/SUB tree to fold +// +// Return Value: +// Returns a new tree or nullptr if nothing is changed. +// +GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, GenTree* tree) +{ + assert(tree->OperIs(GT_ADD, GT_SUB)); + + ValueNumPair vnPair = tree->gtVNPair; + ValueNum vnCnv = vnStore->VNConservativeNormalValue(vnPair); + VNFuncApp funcApp; + + if (!vnStore->IsVNFunc(vnCnv) || !vnStore->GetVNFunc(vnCnv, &funcApp) || !funcApp.FuncIs(VNF_ADD, VNF_SUB) || + vnStore->TypeOfVN(vnCnv) != TYP_INT) + { + return nullptr; + } + + GenTree* foldedTree = fgMorphReduceAddOrSubOps(tree); + + if (foldedTree == tree) + { + return nullptr; + } + + return foldedTree; +} + //------------------------------------------------------------------------------ // optVNBasedFoldExpr: Folds given tree using VN to a constant or a simpler tree. // @@ -2679,7 +2714,9 @@ GenTree* Compiler::optVNBasedFoldExpr(BasicBlock* block, GenTree* parent, GenTre { case GT_CALL: return optVNBasedFoldExpr_Call(block, parent, tree->AsCall()); - + case GT_ADD: + case GT_SUB: + return optVNBasedFoldExpr_AddSub(block, parent, tree); // We can add more VN-based foldings here. default: diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2e0da4b195bc09..9d75b3283f9f80 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8645,6 +8645,7 @@ class Compiler GenTree* optVNBasedFoldExpr_Call_Memmove(GenTreeCall* call); GenTree* optVNBasedFoldExpr_Call_Memset(GenTreeCall* call); GenTree* optVNBasedFoldExpr_Call_Memcmp(GenTreeCall* call); + GenTree* optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, GenTree* tree); AssertionIndex GetAssertionCount() { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index c021c56708ae3f..d7941109219a50 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7522,7 +7522,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA { GenTree* morphed = fgMorphReduceAddOrSubOps(tree); if (morphed != tree) + { + DEBUG_DESTROY_NODE(tree); return fgMorphTree(morphed); + } } /*------------------------------------------------------------------------- @@ -15566,10 +15569,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); - GenTree* morphed = gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); - DEBUG_DESTROY_NODE(tree); - - return morphed; + return gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); } //------------------------------------------------------------------------ From 89dea9e5619f2f10612c9308d5e5b889ea943278 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Thu, 9 Apr 2026 16:27:38 +0200 Subject: [PATCH 10/22] fix assertion --- src/coreclr/jit/assertionprop.cpp | 15 +++++++++++++-- src/coreclr/jit/morph.cpp | 1 - 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index e01dbd92811b6a..75c2c0fd07572c 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2672,14 +2672,25 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, ValueNumPair vnPair = tree->gtVNPair; ValueNum vnCnv = vnStore->VNConservativeNormalValue(vnPair); - VNFuncApp funcApp; + VNFuncApp vnFuncApp; - if (!vnStore->IsVNFunc(vnCnv) || !vnStore->GetVNFunc(vnCnv, &funcApp) || !funcApp.FuncIs(VNF_ADD, VNF_SUB) || + if (!vnStore->GetVNFunc(vnCnv, &vnFuncApp) || !vnFuncApp.FuncIs(VNF_ADD, VNF_SUB) || vnStore->TypeOfVN(vnCnv) != TYP_INT) { return nullptr; } + ValueNum vnOp1 = vnFuncApp.m_args[0]; + ValueNum vnOp2 = vnFuncApp.m_args[1]; + VNFuncApp vnFuncAppOp1; + VNFuncApp vnFuncAppOp2; + + if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_ADD, VNF_SUB) || + !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_ADD, VNF_SUB)) + { + return nullptr; + } + GenTree* foldedTree = fgMorphReduceAddOrSubOps(tree); if (foldedTree == tree) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index d7941109219a50..7414a4d3a7a755 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15568,7 +15568,6 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) // V0 - V0 ... - V0 becomes V0 * (- foldCount + 2), where postorder transform will optimize // accordingly consTree->BashToConst(foldCount, tree->TypeGet()); - return gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); } From 3620c6bf4f588234b02f60498849b647beebc29a Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Thu, 9 Apr 2026 16:53:51 +0200 Subject: [PATCH 11/22] missing memory func --- src/coreclr/jit/assertionprop.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 75c2c0fd07572c..26803ae9707041 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2685,8 +2685,9 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, VNFuncApp vnFuncAppOp1; VNFuncApp vnFuncAppOp2; - if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_ADD, VNF_SUB) || - !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_ADD, VNF_SUB)) + if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || + !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB) || + !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB)) { return nullptr; } From d0e797547379d078d21abd4b357bfebd2c2a4c4c Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Fri, 10 Apr 2026 16:14:15 +0200 Subject: [PATCH 12/22] call morph from assertionprop --- src/coreclr/jit/assertionprop.cpp | 16 +++- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/gentree.h | 65 -------------- src/coreclr/jit/morph.cpp | 139 ++++++++++++++++++++++++++---- 4 files changed, 138 insertions(+), 85 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 26803ae9707041..071ccd72b224d0 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2684,10 +2684,20 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, ValueNum vnOp2 = vnFuncApp.m_args[1]; VNFuncApp vnFuncAppOp1; VNFuncApp vnFuncAppOp2; + int cns = 0; - if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || - !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB) || - !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB)) + if (vnStore->IsVNIntegralConstant(vnOp1, &cns) && cns == 0) + { + return tree->OperIs(GT_ADD) ? tree->gtGetOp2() : gtNewOperNode(GT_NEG, tree->TypeGet(), tree->gtGetOp2()); + } + else if (vnStore->IsVNIntegralConstant(vnOp2, &cns) && cns == 0) + { + return tree->gtGetOp1(); + } + else if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || + !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB) || + !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || + !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB)) { return nullptr; } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ca393a7f5ff4e4..f072d9aa76da43 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1722,7 +1722,7 @@ struct FuncInfoDsc }; jitstd::vector* funWasmLocalDecls; -#endif // defined(TARGET_WASM) +#endif // defined(TARGET_WASM) EHblkDsc* GetEHDesc(Compiler* comp) const; BasicBlock* GetStartBlock(Compiler* comp) const; @@ -6887,6 +6887,7 @@ class Compiler GenTreeOp* fgMorphCommutative(GenTreeOp* tree); GenTree* fgMorphReduceAddOrSubOps(GenTree* tree); + bool fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, unsigned int* lclNum); public: GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 0a13c1bd681a95..178cfd3c48e864 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2013,8 +2013,6 @@ struct GenTree // Sets the GTF flag equivalent for the regIndex'th register of a multi-reg node. void SetRegSpillFlagByIdx(GenTreeFlags flags, int regIndex); - bool OperIsIntScalarMulLclVar(ssize_t* scalar, unsigned int* lclNum); - #ifdef TARGET_ARM64 bool NeedsConsecutiveRegisters() const; #endif @@ -10584,69 +10582,6 @@ inline GenTree* GenTree::GetIndirOrArrMetaDataAddr() } } -inline bool GenTree::OperIsIntScalarMulLclVar(ssize_t* scalar, unsigned int* lclNum) -{ - if (OperIs(GT_LCL_VAR)) - { - *scalar = 1; - *lclNum = AsLclVar()->GetLclNum(); - return true; - } - else if (OperIs(GT_IND) && AsIndir()->Base()->OperIs(GT_LCL_VAR)) - { - *scalar = 1; - *lclNum = AsIndir()->Base()->AsLclVar()->GetLclNum(); - return true; - } - - GenTree* op = this; - bool reverseSign = false; - if (OperIs(GT_NEG)) - { - op = op->gtGetOp1(); - reverseSign = true; - } - - if (!op->OperIs(GT_MUL)) - { - return false; - } - - GenTree* op1 = op->gtGetOp1(); - GenTree* op2 = op->gtGetOp2(); - - if (op1->IsIntegralConst() && op2->OperIs(GT_LCL_VAR)) - { - *scalar = op1->AsIntConCommon()->IconValue(); - *lclNum = op2->AsLclVar()->GetLclNum(); - } - else if (op2->IsIntegralConst() && op1->OperIs(GT_LCL_VAR)) - { - *scalar = op2->AsIntConCommon()->IconValue(); - *lclNum = op1->AsLclVar()->GetLclNum(); - } - else if (op1->IsIntegralConst() && op2->OperIs(GT_IND) && op2->AsIndir()->Base()->OperIs(GT_LCL_VAR)) - { - *scalar = op1->AsIntConCommon()->IconValue(); - *lclNum = op2->AsIndir()->Base()->AsLclVar()->GetLclNum(); - } - else if (op2->IsIntegralConst() && op1->OperIs(GT_IND) && op1->AsIndir()->Base()->OperIs(GT_LCL_VAR)) - { - *scalar = op2->AsIntConCommon()->IconValue(); - *lclNum = op1->AsIndir()->Base()->AsLclVar()->GetLclNum(); - } - else - { - return false; - } - - if (reverseSign) - { - *scalar = -(*scalar); - } - return true; -} - /*****************************************************************************/ #ifndef HOST_64BIT diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 7414a4d3a7a755..1276055d32d2d8 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7518,16 +7518,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA break; } - if (opts.OptimizationEnabled() && fgGlobalMorph) - { - GenTree* morphed = fgMorphReduceAddOrSubOps(tree); - if (morphed != tree) - { - DEBUG_DESTROY_NODE(tree); - return fgMorphTree(morphed); - } - } - /*------------------------------------------------------------------------- * Process the first operand, if any */ @@ -15478,6 +15468,117 @@ bool Compiler::fgCanTailCallViaJitHelper(GenTreeCall* call) #endif } +//------------------------------------------------------------------------ +// fgMorphOperIsIntScalarMulLclVar: detect if op is a variable or multiple of a variable, +// e.g., i => true. +// e.g., -i => true. +// e.g., -2 * i => true. +// e.g., 5 * i + 1 => false. +// +// Arguments: +// op - Operation to analyze +// scalar - the scalar to which variable is multiplied +// lclNum - lclNum of the variable +// +// Return Value: +// true if pattern matches and false otherwise +// +bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, unsigned int* lclNum) +{ + if (op->OperIs(GT_LCL_VAR)) + { + *scalar = 1; + *lclNum = op->AsLclVar()->GetLclNum(); + return true; + } + else if (op->OperIs(GT_COMMA) && op->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = 1; + *lclNum = op->gtGetOp1()->AsLclVar()->GetLclNum(); + return true; + } + + GenTree* internalOp = op; + bool reverseSign = false; + if (op->OperIs(GT_NEG)) + { + internalOp = op->gtGetOp1(); + reverseSign = true; + } + + if (internalOp->OperIs(GT_LSH) && internalOp->gtGetOp2()->IsIntegralConst()) + { + GenTree* op1 = internalOp->gtGetOp1(); + ssize_t temp_scalar = static_cast(pow(2, internalOp->gtGetOp2()->AsIntConCommon()->IconValue())); + + if (op1->OperIs(GT_LCL_VAR)) + { + *scalar = temp_scalar; + *lclNum = op1->AsLclVar()->GetLclNum(); + } + else if (op1->OperIs(GT_NEG) && op1->gtGetOp1()->OperIs(GT_LCL_VAR)) + { + *scalar = -temp_scalar; + *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = temp_scalar; + *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else if (op1->OperIs(GT_NEG) && op1->gtGetOp1()->OperIs(GT_COMMA) && + op1->gtGetOp1()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = -temp_scalar; + *lclNum = op1->gtGetOp1()->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else + { + return false; + } + } + else if (internalOp->OperIs(GT_MUL)) + { + GenTree* op1 = internalOp->gtGetOp1(); + GenTree* op2 = internalOp->gtGetOp2(); + + if (op1->IsIntegralConst() && op2->OperIs(GT_LCL_VAR)) + { + *scalar = op1->AsIntConCommon()->IconValue(); + *lclNum = op2->AsLclVar()->GetLclNum(); + } + else if (op2->IsIntegralConst() && op1->OperIs(GT_LCL_VAR)) + { + *scalar = op2->AsIntConCommon()->IconValue(); + *lclNum = op1->AsLclVar()->GetLclNum(); + } + else if (op1->IsIntegralConst() && op2->OperIs(GT_COMMA) && op2->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = op1->AsIntConCommon()->IconValue(); + *lclNum = op2->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else if (op2->IsIntegralConst() && op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = op2->AsIntConCommon()->IconValue(); + *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else + { + return false; + } + } + else + { + return false; + } + + if (reverseSign) + { + *scalar = -(*scalar); + } + return true; +} + //------------------------------------------------------------------------ // fgMorphReduceAddOrSubOps: reduce successive variable adds/subs into a single multiply, // e.g., i + 3 * i + i - 4 * i + i => i * 2. @@ -15516,7 +15617,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) ssize_t op2Scalar = 0; unsigned int op2lclNum = 0; - if (!op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) || !varTypeIsIntegral(op2)) + if (!fgMorphOperIsIntScalarMulLclVar(op2, &op2Scalar, &op2lclNum) || !varTypeIsIntegral(op2)) { return tree; } @@ -15528,8 +15629,8 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) while (true) { // ADD(lclNum, lclNum) OR SUB(lclNum, lclNum), end of tree - if ((op1->OperIsIntScalarMulLclVar(&op1Scalar, &op1lclNum) && op1lclNum == lclNum) && - (op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) && op2lclNum == lclNum)) + if ((fgMorphOperIsIntScalarMulLclVar(op1, &op1Scalar, &op1lclNum) && op1lclNum == lclNum) && + (fgMorphOperIsIntScalarMulLclVar(op2, &op2Scalar, &op2lclNum) && op2lclNum == lclNum)) { if (targetOp == GT_ADD) { @@ -15543,7 +15644,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) } // ADD(SUB(X, Y), lclNum), keep descending else if (op1->OperIs(GT_ADD, GT_SUB) && !op1->gtOverflow() && - (op2->OperIsIntScalarMulLclVar(&op2Scalar, &op2lclNum) && op2lclNum == lclNum)) + (fgMorphOperIsIntScalarMulLclVar(op2, &op2Scalar, &op2lclNum) && op2lclNum == lclNum)) { if (targetOp == GT_ADD) { @@ -15567,8 +15668,14 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) // V0 + V0 ... + V0 becomes V0 * foldCount, // V0 - V0 ... - V0 becomes V0 * (- foldCount + 2), where postorder transform will optimize // accordingly - consTree->BashToConst(foldCount, tree->TypeGet()); - return gtNewOperNode(GT_MUL, tree->TypeGet(), lclVarTree, consTree); + + if (foldCount == -1) + { + return gtNewOperNode(GT_NEG, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH) ? op1->gtGetOp1() : op1); + } + + return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH) ? op1->gtGetOp1() : op1, + gtNewIconNode(foldCount, tree->TypeGet())); } //------------------------------------------------------------------------ From 33d76b63f25c8b570fe4671f5ee25a09875b27f7 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Fri, 10 Apr 2026 16:18:16 +0200 Subject: [PATCH 13/22] rollback unwated change --- src/coreclr/jit/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f072d9aa76da43..2d49a345a59a95 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1722,7 +1722,7 @@ struct FuncInfoDsc }; jitstd::vector* funWasmLocalDecls; -#endif // defined(TARGET_WASM) +#endif // defined(TARGET_WASM) EHblkDsc* GetEHDesc(Compiler* comp) const; BasicBlock* GetStartBlock(Compiler* comp) const; From b90bc839a159c0f33a3fe15576f51e93d66f5fd8 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Wed, 15 Apr 2026 16:51:15 +0200 Subject: [PATCH 14/22] handle case of 0 --- src/coreclr/jit/morph.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1276055d32d2d8..3332c6b2dac4a5 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15567,6 +15567,28 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns return false; } } + else if (internalOp->OperIs(GT_XOR)) + { + GenTree* op1 = internalOp->gtGetOp1(); + GenTree* op2 = internalOp->gtGetOp2(); + + if (op1->OperIs(GT_LCL_VAR) && op2->OperIs(GT_LCL_VAR) && + op1->AsLclVar()->GetLclNum() == op2->AsLclVar()->GetLclNum()) + { + *scalar = 0; + *lclNum = op2->AsLclVar()->GetLclNum(); + } + else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && op2->OperIs(GT_LCL_VAR) && + op1->gtGetOp1()->AsLclVar()->GetLclNum() == op2->AsLclVar()->GetLclNum()) + { + *scalar = 0; + *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); + } + else + { + return false; + } + } else { return false; @@ -15668,13 +15690,17 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) // V0 + V0 ... + V0 becomes V0 * foldCount, // V0 - V0 ... - V0 becomes V0 * (- foldCount + 2), where postorder transform will optimize // accordingly - - if (foldCount == -1) + if (foldCount == 0) + { + return gtNewOperNode(GT_XOR, tree->TypeGet(), op1, + op1->OperIs(GT_LCL_VAR) ? op1 : gtCloneExpr(op1->gtEffectiveVal())); + } + else if (foldCount == -1) { - return gtNewOperNode(GT_NEG, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH) ? op1->gtGetOp1() : op1); + return gtNewOperNode(GT_NEG, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1); } - return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH) ? op1->gtGetOp1() : op1, + return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1, gtNewIconNode(foldCount, tree->TypeGet())); } From 52d6a75a839be4637036f096c25f3205e3c9edd3 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Fri, 17 Apr 2026 12:20:51 +0200 Subject: [PATCH 15/22] 0 - x is better than -x --- src/coreclr/jit/assertionprop.cpp | 2 +- src/coreclr/jit/morph.cpp | 50 ++++++++++++++++++------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index b21cb95561a8a9..06f55a0160b4a2 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2687,7 +2687,7 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, if (vnStore->IsVNIntegralConstant(vnOp1, &cns) && cns == 0) { - return tree->OperIs(GT_ADD) ? tree->gtGetOp2() : gtNewOperNode(GT_NEG, tree->TypeGet(), tree->gtGetOp2()); + return tree->OperIs(GT_ADD) ? tree->gtGetOp2() : tree; } else if (vnStore->IsVNIntegralConstant(vnOp2, &cns) && cns == 0) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index f5f2be99bec20c..1907098059579b 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15491,19 +15491,30 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *lclNum = op->gtGetOp1()->AsLclVar()->GetLclNum(); return true; } - - GenTree* internalOp = op; - bool reverseSign = false; - if (op->OperIs(GT_NEG)) + else if (op->OperIs(GT_SUB) && op->gtGetOp1()->IsIntegralConst(0)) { - internalOp = op->gtGetOp1(); - reverseSign = true; + if (op->gtGetOp2()->OperIs(GT_LCL_VAR)) + { + *scalar = 1; + *lclNum = op->gtGetOp2()->AsLclVar()->GetLclNum(); + return true; + } + else if (op->gtGetOp2()->OperIs(GT_COMMA) && op->gtGetOp2()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + { + *scalar = 1; + *lclNum = op->gtGetOp2()->gtGetOp1()->AsLclVar()->GetLclNum(); + return true; + } + else + { + return false; + } } - if (internalOp->OperIs(GT_LSH) && internalOp->gtGetOp2()->IsIntegralConst()) + if (op->OperIs(GT_LSH) && op->gtGetOp2()->IsIntegralConst()) { - GenTree* op1 = internalOp->gtGetOp1(); - ssize_t temp_scalar = static_cast(pow(2, internalOp->gtGetOp2()->AsIntConCommon()->IconValue())); + GenTree* op1 = op->gtGetOp1(); + ssize_t temp_scalar = static_cast(pow(2, op->gtGetOp2()->AsIntConCommon()->IconValue())); if (op1->OperIs(GT_LCL_VAR)) { @@ -15531,10 +15542,10 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns return false; } } - else if (internalOp->OperIs(GT_MUL)) + else if (op->OperIs(GT_MUL)) { - GenTree* op1 = internalOp->gtGetOp1(); - GenTree* op2 = internalOp->gtGetOp2(); + GenTree* op1 = op->gtGetOp1(); + GenTree* op2 = op->gtGetOp2(); if (op1->IsIntegralConst() && op2->OperIs(GT_LCL_VAR)) { @@ -15561,10 +15572,10 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns return false; } } - else if (internalOp->OperIs(GT_XOR)) + else if (op->OperIs(GT_XOR)) { - GenTree* op1 = internalOp->gtGetOp1(); - GenTree* op2 = internalOp->gtGetOp2(); + GenTree* op1 = op->gtGetOp1(); + GenTree* op2 = op->gtGetOp2(); if (op1->OperIs(GT_LCL_VAR) && op2->OperIs(GT_LCL_VAR) && op1->AsLclVar()->GetLclNum() == op2->AsLclVar()->GetLclNum()) @@ -15588,10 +15599,6 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns return false; } - if (reverseSign) - { - *scalar = -(*scalar); - } return true; } @@ -15687,11 +15694,12 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) if (foldCount == 0) { return gtNewOperNode(GT_XOR, tree->TypeGet(), op1, - op1->OperIs(GT_LCL_VAR) ? op1 : gtCloneExpr(op1->gtEffectiveVal())); + gtCloneExpr(op1->OperIs(GT_LCL_VAR) ? op1 : op1->gtEffectiveVal())); } else if (foldCount == -1) { - return gtNewOperNode(GT_NEG, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1); + return gtNewOperNode(GT_SUB, tree->TypeGet(), gtNewZeroConNode(tree->TypeGet()), + op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1); } return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1, From 5cdf3afc08b34be9e7f323f5af61e16b93e581f9 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Fri, 17 Apr 2026 17:24:05 +0200 Subject: [PATCH 16/22] do not optimize long on X86 --- src/coreclr/jit/assertionprop.cpp | 9 +++++++++ src/coreclr/jit/morph.cpp | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 06f55a0160b4a2..25e1085ef83970 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2669,6 +2669,15 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, { assert(tree->OperIs(GT_ADD, GT_SUB)); +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing + // ADD/SUB ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return nullptr; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + ValueNumPair vnPair = tree->gtVNPair; ValueNum vnCnv = vnStore->VNConservativeNormalValue(vnPair); VNFuncApp vnFuncApp; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1907098059579b..1b508da52b351f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15621,15 +15621,6 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) } genTreeOps targetOp = tree->OperGet(); -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing - // ADD/SUB ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return tree; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - GenTree* lclVarTree = tree->AsOp()->gtOp2; GenTree* consTree = tree->AsOp()->gtOp1; From 05b5f93b4258501203b8d53bc97cfbca104622f6 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Fri, 17 Apr 2026 17:32:26 +0200 Subject: [PATCH 17/22] fix formatting --- src/coreclr/jit/morph.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1b508da52b351f..51cdaca973aca4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15620,9 +15620,9 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return tree; } - genTreeOps targetOp = tree->OperGet(); - GenTree* lclVarTree = tree->AsOp()->gtOp2; - GenTree* consTree = tree->AsOp()->gtOp1; + genTreeOps targetOp = tree->OperGet(); + GenTree* lclVarTree = tree->AsOp()->gtOp2; + GenTree* consTree = tree->AsOp()->gtOp1; GenTree* op1 = consTree; ssize_t op1Scalar = 0; From 783c56fbe621434b41acdbc5511340a5319a7808 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Tue, 21 Apr 2026 15:59:21 +0200 Subject: [PATCH 18/22] lea x+x better than lea 2x --- src/coreclr/jit/morph.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 51cdaca973aca4..46edf47eefd80c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15636,8 +15636,9 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return tree; } - ssize_t foldCount = 0; - unsigned int lclNum = op2lclNum; + ssize_t foldCount = 0; + unsigned int lclNum = op2lclNum; + ssize_t addSubsChainLength = 0; // Search for pattern of shape ADD(SUB(ADD(lclNum, lclNum), lclNum), lclNum). while (true) @@ -15654,6 +15655,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) { foldCount += op1Scalar - op2Scalar; } + addSubsChainLength = +op1Scalar + op2Scalar - 1; break; } // ADD(SUB(X, Y), lclNum), keep descending @@ -15671,6 +15673,7 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) targetOp = op1->OperGet(); op2 = op1->AsOp()->gtOp2; op1 = op1->AsOp()->gtOp1; + addSubsChainLength += op2Scalar - 1; } // Any other case is a pattern we won't attempt to fold for now. else @@ -15692,6 +15695,10 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return gtNewOperNode(GT_SUB, tree->TypeGet(), gtNewZeroConNode(tree->TypeGet()), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1); } + else if (foldCount == 2 && addSubsChainLength == 1 && targetOp == GT_ADD) + { + return tree; + } return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1, gtNewIconNode(foldCount, tree->TypeGet())); From efcfb5e24eb580fe1791f3a5d226afceb8853a65 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Tue, 21 Apr 2026 22:47:50 +0200 Subject: [PATCH 19/22] Revert "lea x+x better than lea 2x" This reverts commit 783c56fbe621434b41acdbc5511340a5319a7808. --- src/coreclr/jit/morph.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1ed522a7c5eb8c..fa3f7e0c2e945c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15657,9 +15657,8 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return tree; } - ssize_t foldCount = 0; - unsigned int lclNum = op2lclNum; - ssize_t addSubsChainLength = 0; + ssize_t foldCount = 0; + unsigned int lclNum = op2lclNum; // Search for pattern of shape ADD(SUB(ADD(lclNum, lclNum), lclNum), lclNum). while (true) @@ -15676,7 +15675,6 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) { foldCount += op1Scalar - op2Scalar; } - addSubsChainLength = +op1Scalar + op2Scalar - 1; break; } // ADD(SUB(X, Y), lclNum), keep descending @@ -15694,7 +15692,6 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) targetOp = op1->OperGet(); op2 = op1->AsOp()->gtOp2; op1 = op1->AsOp()->gtOp1; - addSubsChainLength += op2Scalar - 1; } // Any other case is a pattern we won't attempt to fold for now. else @@ -15716,10 +15713,6 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return gtNewOperNode(GT_SUB, tree->TypeGet(), gtNewZeroConNode(tree->TypeGet()), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1); } - else if (foldCount == 2 && addSubsChainLength == 1 && targetOp == GT_ADD) - { - return tree; - } return gtNewOperNode(GT_MUL, tree->TypeGet(), op1->OperIs(GT_MUL, GT_LSH, GT_XOR) ? op1->gtGetOp1() : op1, gtNewIconNode(foldCount, tree->TypeGet())); From ad8fb9bbef93d0295f4dd0061787298edfb10708 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Wed, 22 Apr 2026 07:04:12 +0200 Subject: [PATCH 20/22] rollback moving x86 exclusion --- src/coreclr/jit/assertionprop.cpp | 9 --------- src/coreclr/jit/morph.cpp | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 728e3899eccc9e..606cace93e5e64 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2669,15 +2669,6 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, { assert(tree->OperIs(GT_ADD, GT_SUB)); -#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) - // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing - // ADD/SUB ops with a helper function call. Don't apply optimization in that case. - if (tree->TypeIs(TYP_LONG)) - { - return nullptr; - } -#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) - ValueNumPair vnPair = tree->gtVNPair; ValueNum vnCnv = vnStore->VNConservativeNormalValue(vnPair); VNFuncApp vnFuncApp; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index fa3f7e0c2e945c..64ad10ae0466ee 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15641,6 +15641,15 @@ GenTree* Compiler::fgMorphReduceAddOrSubOps(GenTree* tree) return tree; } +#if !defined(TARGET_64BIT) && !defined(TARGET_WASM) + // Transforming 64-bit ADD/SUB to 64-bit MUL on 32-bit system results in replacing + // ADD/SUB ops with a helper function call. Don't apply optimization in that case. + if (tree->TypeIs(TYP_LONG)) + { + return tree; + } +#endif // !defined(TARGET_64BIT) && !defined(TARGET_WASM) + genTreeOps targetOp = tree->OperGet(); GenTree* lclVarTree = tree->AsOp()->gtOp2; GenTree* consTree = tree->AsOp()->gtOp1; From e71c92723bcb5921851bba36cb0f0ad5493ccd17 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Mon, 27 Apr 2026 16:13:04 +0200 Subject: [PATCH 21/22] minor improvement --- src/coreclr/jit/assertionprop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 606cace93e5e64..3e519c9e8a6f2e 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2694,9 +2694,9 @@ GenTree* Compiler::optVNBasedFoldExpr_AddSub(BasicBlock* block, GenTree* parent, return tree->gtGetOp1(); } else if (!vnStore->GetVNFunc(vnOp1, &vnFuncAppOp1) || - !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB) || + !vnFuncAppOp1.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB, VNF_LSH, VNF_XOR, VNF_MUL) || !vnStore->GetVNFunc(vnOp2, &vnFuncAppOp2) || - !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB)) + !vnFuncAppOp2.FuncIs(VNF_InitVal, VNF_MemOpaque, VNF_ADD, VNF_SUB, VNF_LSH, VNF_XOR, VNF_MUL)) { return nullptr; } From ffeb80ea63e18fc4d39adf73dde7b429729fec77 Mon Sep 17 00:00:00 2001 From: pedrobsaila Date: Wed, 29 Apr 2026 15:53:08 +0200 Subject: [PATCH 22/22] add constraint --- src/coreclr/jit/morph.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 64ad10ae0466ee..bc1cdc42946efa 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -15506,7 +15506,8 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *lclNum = op->AsLclVar()->GetLclNum(); return true; } - else if (op->OperIs(GT_COMMA) && op->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + else if (op->OperIs(GT_COMMA) && op->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = 1; *lclNum = op->gtGetOp1()->AsLclVar()->GetLclNum(); @@ -15520,7 +15521,8 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *lclNum = op->gtGetOp2()->AsLclVar()->GetLclNum(); return true; } - else if (op->gtGetOp2()->OperIs(GT_COMMA) && op->gtGetOp2()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + else if (op->gtGetOp2()->OperIs(GT_COMMA) && op->gtGetOp2()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op->gtGetOp2()->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = 1; *lclNum = op->gtGetOp2()->gtGetOp1()->AsLclVar()->GetLclNum(); @@ -15547,13 +15549,15 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *scalar = -temp_scalar; *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); } - else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op1->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = temp_scalar; *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); } else if (op1->OperIs(GT_NEG) && op1->gtGetOp1()->OperIs(GT_COMMA) && - op1->gtGetOp1()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + op1->gtGetOp1()->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op1->gtGetOp1()->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = -temp_scalar; *lclNum = op1->gtGetOp1()->gtGetOp1()->AsLclVar()->GetLclNum(); @@ -15578,12 +15582,14 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *scalar = op2->AsIntConCommon()->IconValue(); *lclNum = op1->AsLclVar()->GetLclNum(); } - else if (op1->IsIntegralConst() && op2->OperIs(GT_COMMA) && op2->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + else if (op1->IsIntegralConst() && op2->OperIs(GT_COMMA) && op2->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op2->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = op1->AsIntConCommon()->IconValue(); *lclNum = op2->gtGetOp1()->AsLclVar()->GetLclNum(); } - else if (op2->IsIntegralConst() && op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR)) + else if (op2->IsIntegralConst() && op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op1->gtGetOp1()->gtGetOp1()->OperIs(GT_IND)) { *scalar = op2->AsIntConCommon()->IconValue(); *lclNum = op1->gtGetOp1()->AsLclVar()->GetLclNum(); @@ -15604,7 +15610,8 @@ bool Compiler::fgMorphOperIsIntScalarMulLclVar(GenTree* op, ssize_t* scalar, uns *scalar = 0; *lclNum = op2->AsLclVar()->GetLclNum(); } - else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && op2->OperIs(GT_LCL_VAR) && + else if (op1->OperIs(GT_COMMA) && op1->gtGetOp1()->OperIs(GT_STORE_LCL_VAR) && + op1->gtGetOp1()->gtGetOp1()->OperIs(GT_IND) && op2->OperIs(GT_LCL_VAR) && op1->gtGetOp1()->AsLclVar()->GetLclNum() == op2->AsLclVar()->GetLclNum()) { *scalar = 0;