From cf375548dee4d3094d8b1037d92e9a3e3d4e2282 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 14 May 2026 09:37:26 -0700 Subject: [PATCH 1/7] Move some GT_SUB simplifications from morph to gtFoldExpr --- src/coreclr/jit/gentree.cpp | 45 ++++++++++++++++++++++++++++++- src/coreclr/jit/gentree.h | 5 ++++ src/coreclr/jit/morph.cpp | 53 ------------------------------------- 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 8a0215b99d2310..0632270a54e720 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15113,12 +15113,55 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) return gtFoldExprSpecial(tree); } } - else if (tree->OperIsCompare()) + + genTreeOps oper = tree->OperGet(); + + if (GenTree::OperIsCompare(oper)) { // comparisons of two local variables can sometimes be folded return gtFoldExprCompare(tree); } + switch (oper) + { + case GT_SUB: + { + if (tree->gtOverflow()) + { + break; + } + + if (op2->OperIs(GT_NEG)) + { + if (op1->OperIs(GT_NEG)) + { + JITDUMP("Folding (-a) - (-b) => b - a\n") + + tree->gtOp1 = op2->gtGetOp1(); + tree->gtOp2 = op1->gtGetOp1(); + + tree->ToggleReverseOp(); + return tree; + } + else + { + JITDUMP("Folding a - (-b) => a + b\n") + + tree->gtOp2 = op2->gtGetOp1(); + tree->SetOper(GT_ADD, GenTree::PRESERVE_VN); + + return tree; + } + } + break; + } + + default: + { + break; + } + } + return tree; } diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index a71ed68351c9b4..5c4b87374bb4c1 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2265,6 +2265,11 @@ struct GenTree gtFlags &= ~GTF_REVERSE_OPS; } + void ToggleReverseOp() + { + gtFlags ^= GTF_REVERSE_OPS; + } + #if defined(TARGET_XARCH) void SetDontExtend() { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4645bf4d052b25..b351c3df6829cb 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8003,59 +8003,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, bool* optAssertionPropDone) goto CM_ADD_OP; } } - - // Skip optimization if non-NEG operand is constant. - // Both op1 and op2 are not constant because it was already checked above. - if (opts.OptimizationEnabled()) - { - if (!op2->OperIs(GT_NEG)) - { - break; - } - - if (op1->OperIs(GT_NEG) && gtCanSwapOrder(op1, op2)) - { - // -a - -b = > b - a - // SUB(NEG(a), NEG(b)) => SUB(b, a) - - // tree: SUB - // op1: NEG - // op1Child: a - // op2: NEG - // op2Child: b - - GenTree* op1Child = op1->AsOp()->gtOp1; // a - GenTree* op2Child = op2->AsOp()->gtOp1; // b - tree->AsOp()->gtOp1 = op2Child; - tree->AsOp()->gtOp2 = op1Child; - - DEBUG_DESTROY_NODE(op1); - DEBUG_DESTROY_NODE(op2); - - op1 = op2Child; - op2 = op1Child; - } - else - { - // a - -b = > a + b - // SUB(a, NEG(b)) => ADD(a, b) - - // tree: SUB - // op1: a - // op2: NEG - // op2Child: b - - GenTree* op2Child = op2->AsOp()->gtOp1; // b - oper = GT_ADD; - tree->SetOper(oper, GenTree::PRESERVE_VN); - tree->AsOp()->gtOp2 = op2Child; - - DEBUG_DESTROY_NODE(op2); - - op2 = op2Child; - } - } - break; case GT_DIV: From 3a34f41af2da207cf0dee2adc6e21fc5f3fecd15 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 14 May 2026 07:57:28 -0700 Subject: [PATCH 2/7] Move some GT_ADD simplifications from morph to gtFoldExpr --- src/coreclr/jit/gentree.cpp | 32 ++++++++++++++++++++++++++++++++ src/coreclr/jit/morph.cpp | 25 ------------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 0632270a54e720..abf8762fed87fb 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15124,6 +15124,38 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) switch (oper) { + case GT_ADD: + { + if (tree->gtOverflow()) + { + break; + } + + if (op1->OperIs(GT_NEG)) + { + JITDUMP("Folding (-a) + b => b - a\n") + + tree->gtOp1 = op2; + tree->gtOp2 = op1->gtGetOp1(); + + tree->SetOper(GT_SUB, GenTree::PRESERVE_VN); + tree->ToggleReverseOp(); + + return tree; + } + + if (op2->OperIs(GT_NEG)) + { + JITDUMP("Folding a + (-b) => a - b\n") + + tree->gtOp2 = op2->gtGetOp1(); + tree->SetOper(GT_SUB, GenTree::PRESERVE_VN); + + return tree; + } + break; + } + case GT_SUB: { if (tree->gtOverflow()) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b351c3df6829cb..7e633d59b32e2c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -10565,31 +10565,6 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add) return fgMorphTree(add); } } - - // - a + b => b - a - // ADD(NEG(a), b) => SUB(b, a) - // Do not do this if "op2" is constant for canonicalization purposes. - if (!op2->IsIntegralConst() && gtCanSwapOrder(op1, op2)) - { - add->SetOper(GT_SUB); - add->gtOp1 = op2; - add->gtOp2 = op1->AsOp()->gtGetOp1(); - - DEBUG_DESTROY_NODE(op1); - return add; - } - } - - // a + -b = > a - b - // ADD(a, NEG(b)) => SUB(a, b) - if (!op1->OperIs(GT_NEG) && op2->OperIs(GT_NEG)) - { - add->SetOper(GT_SUB); - add->gtOp2 = op2->AsOp()->gtGetOp1(); - - DEBUG_DESTROY_NODE(op2); - - return add; } // Fold (~x + 1) to -x. From 148c167383e0b411175c530f865f163ad6b6f2da Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sun, 17 May 2026 06:56:51 -0700 Subject: [PATCH 3/7] Don't fold for T0 opts --- src/coreclr/jit/gentree.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index abf8762fed87fb..60607afb36bd7d 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15122,6 +15122,11 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) return gtFoldExprCompare(tree); } + if (!opts.OptimizationEnabled()) + { + return tree; + } + switch (oper) { case GT_ADD: From 805470d0cf7438cda1d42c63578cb029a8ca55b7 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 18 May 2026 18:02:40 -0700 Subject: [PATCH 4/7] Push constants right as part of gtFoldExpr --- src/coreclr/jit/gentree.cpp | 31 ++++++++++++++++--------------- src/coreclr/jit/morph.cpp | 8 -------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 60607afb36bd7d..f266e63d96fbc0 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15099,19 +15099,14 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) // both nodes are constants - fold the expression return gtFoldExprBinaryConst(tree); } - else if (opts.OptimizationEnabled()) + else { - // Too heavy for tier0, so only do when opts are enabled return gtFoldExprSpecial(tree); } } else if (op2->OperIsConst()) { - if (opts.OptimizationEnabled()) - { - // Too heavy for tier0, so only do when opts are enabled - return gtFoldExprSpecial(tree); - } + return gtFoldExprSpecial(tree); } genTreeOps oper = tree->OperGet(); @@ -15913,6 +15908,20 @@ CORINFO_METHOD_HANDLE Compiler::gtGetHelperArgMethodHandle(GenTree* tree) // GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) { + assert(tree->OperIsBinary()); + assert(!optValnumCSE_phase); + assert(opts.Tier0OptimizationEnabled()); + + if (tree->OperIsCommutative() || tree->OperIsCompare()) + { + fgPushConstantsRight(tree); + } + + if (opts.OptimizationDisabled()) + { + return tree; + } + var_types type = tree->TypeGet(); GenTree* op1 = tree->AsOp()->gtOp1; GenTree* op2 = tree->AsOp()->gtOp2; @@ -15922,14 +15931,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) GenTree* cons; ssize_t val; - assert(tree->OperKind() & GTK_BINOP); - - /* Filter out operators that cannot be folded here */ - if (oper == GT_CAST) - { - return tree; - } - /* We only consider TYP_INT for folding * Do not fold pointer arithmetic (e.g. addressing modes!) */ diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 7e633d59b32e2c..bfc85746625269 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7858,9 +7858,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, bool* optAssertionPropDone) case GT_EQ: case GT_NE: { - fgPushConstantsRight(tree->AsOp()); - assert(tree->OperIsCompare()); - oper = tree->OperGet(); op1 = tree->gtGetOp1(); op2 = tree->gtGetOp2(); @@ -7882,9 +7879,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, bool* optAssertionPropDone) case GT_GE: case GT_GT: { - fgPushConstantsRight(tree->AsOp()); - assert(tree->OperIsCompare()); - oper = tree->OperGet(); op1 = tree->gtGetOp1(); op2 = tree->gtGetOp2(); @@ -10395,8 +10389,6 @@ GenTree* Compiler::fgOptimizeCommutativeArithmetic(GenTreeOp* tree) assert(tree->OperIs(GT_ADD, GT_MUL, GT_OR, GT_XOR, GT_AND)); assert(!tree->gtOverflowEx()); - fgPushConstantsRight(tree); - if (fgOperIsBitwiseRotationRoot(tree->OperGet())) { GenTree* rotationTree = fgRecognizeAndMorphBitwiseRotation(tree); From e392f2a0fe6730e69e93e0aac518ea505639ee69 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 18 May 2026 18:18:22 -0700 Subject: [PATCH 5/7] Add missing semicolon --- src/coreclr/jit/gentree.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index f266e63d96fbc0..7997c1a874d051 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15040,7 +15040,7 @@ GenTree* Compiler::gtFoldExprUnary(GenTreeUnOp* tree) { if (op1->OperIs(oper)) { - JITDUMP("Folding ~(~a) => a\n") + JITDUMP("Folding ~(~a) => a\n"); return op1->gtGetOp1(); } break; @@ -15050,7 +15050,7 @@ GenTree* Compiler::gtFoldExprUnary(GenTreeUnOp* tree) { if (op1->OperIs(oper)) { - JITDUMP("Folding -(-a) => a\n") + JITDUMP("Folding -(-a) => a\n"); return op1->gtGetOp1(); } break; @@ -15133,7 +15133,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) if (op1->OperIs(GT_NEG)) { - JITDUMP("Folding (-a) + b => b - a\n") + JITDUMP("Folding (-a) + b => b - a\n"); tree->gtOp1 = op2; tree->gtOp2 = op1->gtGetOp1(); @@ -15146,7 +15146,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) if (op2->OperIs(GT_NEG)) { - JITDUMP("Folding a + (-b) => a - b\n") + JITDUMP("Folding a + (-b) => a - b\n"); tree->gtOp2 = op2->gtGetOp1(); tree->SetOper(GT_SUB, GenTree::PRESERVE_VN); @@ -15167,7 +15167,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) { if (op1->OperIs(GT_NEG)) { - JITDUMP("Folding (-a) - (-b) => b - a\n") + JITDUMP("Folding (-a) - (-b) => b - a\n"); tree->gtOp1 = op2->gtGetOp1(); tree->gtOp2 = op1->gtGetOp1(); @@ -15177,7 +15177,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) } else { - JITDUMP("Folding a - (-b) => a + b\n") + JITDUMP("Folding a - (-b) => a + b\n"); tree->gtOp2 = op2->gtGetOp1(); tree->SetOper(GT_ADD, GenTree::PRESERVE_VN); From 86e52921f34304cb888a040601ac7da17998b3f0 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 18 May 2026 19:16:18 -0700 Subject: [PATCH 6/7] Fix build failure --- src/coreclr/jit/compiler.h | 4 ++-- src/coreclr/jit/gentree.cpp | 31 ++++++++++++++++--------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5101144aefe193..32a983ff52bbb3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3969,8 +3969,8 @@ class Compiler GenTree* gtFoldExprForOverflow(GenTree* tree); GenTree* gtFoldIndirConst(GenTreeIndir* indir); - GenTree* gtFoldExprSpecial(GenTree* tree); - GenTree* gtFoldExprSpecialFloating(GenTree* tree); + GenTree* gtFoldExprSpecial(GenTreeOp* tree); + GenTree* gtFoldExprSpecialFloating(GenTreeOp* tree); GenTree* gtFoldBoxNullable(GenTree* tree); GenTree* gtFoldExprCompare(GenTree* tree); GenTree* gtFoldExprConditional(GenTree* tree); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7997c1a874d051..669f3c1c0c09ae 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15906,10 +15906,9 @@ CORINFO_METHOD_HANDLE Compiler::gtGetHelperArgMethodHandle(GenTree* tree) // Tree (possibly modified at root or below), or a new tree // Any new tree is fully morphed, if necessary. // -GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) +GenTree* Compiler::gtFoldExprSpecial(GenTreeOp* tree) { assert(tree->OperIsBinary()); - assert(!optValnumCSE_phase); assert(opts.Tier0OptimizationEnabled()); if (tree->OperIsCommutative() || tree->OperIsCompare()) @@ -15922,18 +15921,12 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) return tree; } - var_types type = tree->TypeGet(); - GenTree* op1 = tree->AsOp()->gtOp1; - GenTree* op2 = tree->AsOp()->gtOp2; - genTreeOps oper = tree->OperGet(); - - GenTree* op; - GenTree* cons; - ssize_t val; - /* We only consider TYP_INT for folding * Do not fold pointer arithmetic (e.g. addressing modes!) */ + var_types type = tree->TypeGet(); + genTreeOps oper = tree->OperGet(); + if (oper != GT_QMARK && !varTypeIsIntOrI(type)) { if (varTypeIsFloating(type)) @@ -15943,6 +15936,13 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) return tree; } + GenTree* op1 = tree->AsOp()->gtOp1; + GenTree* op2 = tree->AsOp()->gtOp2; + + GenTree* op; + GenTree* cons; + ssize_t val; + /* Find out which is the constant node */ if (op1->IsCnsIntOrI()) @@ -16269,13 +16269,14 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) // Tree (possibly modified at root or below), or a new tree // Any new tree is fully morphed, if necessary. // -GenTree* Compiler::gtFoldExprSpecialFloating(GenTree* tree) +GenTree* Compiler::gtFoldExprSpecialFloating(GenTreeOp* tree) { + assert(tree->OperIsBinary()); + assert(opts.OptimizationEnabled()); assert(varTypeIsFloating(tree->TypeGet())); - assert(tree->OperKind() & GTK_BINOP); - GenTree* op1 = tree->AsOp()->gtOp1; - GenTree* op2 = tree->AsOp()->gtOp2; + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); genTreeOps oper = tree->OperGet(); GenTree* op; From 8559031d0bd9b88d6132f95a5a1228ef18d4cbb1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 18 May 2026 20:23:39 -0700 Subject: [PATCH 7/7] Don't fold ops that need reverseops on wasm --- src/coreclr/jit/gentree.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 669f3c1c0c09ae..980afaa6b57be9 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15131,6 +15131,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) break; } +#if !defined(TARGET_WASM) if (op1->OperIs(GT_NEG)) { JITDUMP("Folding (-a) + b => b - a\n"); @@ -15143,6 +15144,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) return tree; } +#endif if (op2->OperIs(GT_NEG)) { @@ -15165,6 +15167,7 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) if (op2->OperIs(GT_NEG)) { +#if !defined(TARGET_WASM) if (op1->OperIs(GT_NEG)) { JITDUMP("Folding (-a) - (-b) => b - a\n"); @@ -15175,15 +15178,14 @@ GenTree* Compiler::gtFoldExprBinary(GenTreeOp* tree) tree->ToggleReverseOp(); return tree; } - else - { - JITDUMP("Folding a - (-b) => a + b\n"); +#endif - tree->gtOp2 = op2->gtGetOp1(); - tree->SetOper(GT_ADD, GenTree::PRESERVE_VN); + JITDUMP("Folding a - (-b) => a + b\n"); - return tree; - } + tree->gtOp2 = op2->gtGetOp1(); + tree->SetOper(GT_ADD, GenTree::PRESERVE_VN); + + return tree; } break; }