From 252c80a4f4bf03c2cbe879b1225fcf75f99caed9 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 10 Apr 2023 19:02:12 -0700 Subject: [PATCH 01/13] Combine compare and shift ops into a single compare op --- src/coreclr/jit/codegen.h | 1 + src/coreclr/jit/codegenarm64.cpp | 60 +++++++++++++++++++------------- src/coreclr/jit/lowerarmarch.cpp | 20 +++++++++++ 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index f86d7db1e6b305..cefb5f5c2052e5 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1552,6 +1552,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #if defined(TARGET_ARM64) static insCond JumpKindToInsCond(emitJumpKind condition); + static insOpts ShiftOpToInsOpts(genTreeOps op); #elif defined(TARGET_XARCH) static instruction JumpKindToCmov(emitJumpKind condition); #endif diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 6b1a20565dec2c..d47b1c0a043498 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -2626,31 +2626,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree) } } - switch (op2->gtOper) - { - case GT_LSH: - { - opt = INS_OPTS_LSL; - break; - } - - case GT_RSH: - { - opt = INS_OPTS_ASR; - break; - } - - case GT_RSZ: - { - opt = INS_OPTS_LSR; - break; - } - - default: - { - unreached(); - } - } + opt = ShiftOpToInsOpts(op2->gtOper); emit->emitIns_R_R_R_I(ins, emitActualTypeSize(tree), targetReg, a->GetRegNum(), b->GetRegNum(), c->AsIntConCommon()->IconValue(), opt); @@ -4546,6 +4522,15 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) emit->emitIns_R_I(ins, cmpSize, op1Reg, intConst->IconValue()); } + else if (op2->isContained()) + { + assert(op2->OperIs(GT_LSH, GT_RSH, GT_RSZ)); + assert(op2->gtGetOp2()->IsCnsIntOrI()); + assert(op2->gtGetOp2()->isContained()); + + emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(), + op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(op2->gtOper)); + } else { emit->emitIns_R_R(ins, cmpSize, op1->GetRegNum(), op2->GetRegNum()); @@ -10329,4 +10314,29 @@ insCond CodeGen::JumpKindToInsCond(emitJumpKind condition) } } +//------------------------------------------------------------------------ +// ShiftOpToInsOpts: Convert a shift-op to a insOpts. +// +// Arguments: +// shiftOp - the shift-op tree. +// +insOpts CodeGen::ShiftOpToInsOpts(genTreeOps op) +{ + switch (op) + { + case GT_LSH: + return INS_OPTS_LSL; + + case GT_RSH: + return INS_OPTS_ASR; + + case GT_RSZ: + return INS_OPTS_LSR; + + default: + NO_WAY("expected a shift-op"); + return INS_OPTS_NONE; + } +} + #endif // TARGET_ARM64 diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 0a9f57f5469264..115184a3e52170 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -2265,6 +2265,26 @@ void Lowering::ContainCheckCast(GenTreeCast* node) void Lowering::ContainCheckCompare(GenTreeOp* cmp) { CheckImmedAndMakeContained(cmp, cmp->gtOp2); + +#ifdef TARGET_ARM64 + if (comp->opts.OptimizationEnabled() && cmp->gtGetOp2()->OperIs(GT_LSH, GT_RSH, GT_RSZ)) + { + auto isValidImmForShift = [](ssize_t imm, var_types type) + { return emitter::isValidImmShift(imm, EA_ATTR(genTypeSize(type))); }; + + GenTree* op2 = cmp->gtGetOp2(); + + if (op2->gtGetOp2()->IsCnsIntOrI() && + isValidImmForShift(op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), cmp->TypeGet())) + { + op2->ClearContained(); + op2->gtGetOp1()->ClearContained(); + + MakeSrcContained(op2, op2->gtGetOp2()); + MakeSrcContained(cmp, op2); + } + } +#endif // TARGET_ARM64 } #ifdef TARGET_ARM64 From 6455122d33d02caaa902791c7c8ba195e5cc86fb Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 10 Apr 2023 19:09:38 -0700 Subject: [PATCH 02/13] Fix comment --- src/coreclr/jit/codegenarm64.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index d47b1c0a043498..3bfe03b88ba8f3 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -10318,11 +10318,11 @@ insCond CodeGen::JumpKindToInsCond(emitJumpKind condition) // ShiftOpToInsOpts: Convert a shift-op to a insOpts. // // Arguments: -// shiftOp - the shift-op tree. +// shiftOp - the shift-op. // -insOpts CodeGen::ShiftOpToInsOpts(genTreeOps op) +insOpts CodeGen::ShiftOpToInsOpts(genTreeOps shiftOp) { - switch (op) + switch (shiftOp) { case GT_LSH: return INS_OPTS_LSL; From cb932ba1abf6bb9e5a810b6c178179635aa69256 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 10 Apr 2023 20:10:00 -0700 Subject: [PATCH 03/13] Expanding IsContainableBinaryOp. --- src/coreclr/jit/lowerarmarch.cpp | 68 +++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 115184a3e52170..58f5364fe89e12 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -269,7 +269,7 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co return false; } - if (parentNode->OperIs(GT_CMP, GT_OR, GT_XOR)) + if (parentNode->OperIs(GT_CMP, GT_OR, GT_XOR) || parentNode->OperIsCompare()) { if (IsInvariantInRange(childNode, parentNode)) { @@ -2264,27 +2264,67 @@ void Lowering::ContainCheckCast(GenTreeCast* node) // void Lowering::ContainCheckCompare(GenTreeOp* cmp) { - CheckImmedAndMakeContained(cmp, cmp->gtOp2); + GenTree* op1 = cmp->gtGetOp1(); + GenTree* op2 = cmp->gtGetOp2(); + + if (CheckImmedAndMakeContained(cmp, op2)) + return; #ifdef TARGET_ARM64 - if (comp->opts.OptimizationEnabled() && cmp->gtGetOp2()->OperIs(GT_LSH, GT_RSH, GT_RSZ)) + if (comp->opts.OptimizationEnabled()) { - auto isValidImmForShift = [](ssize_t imm, var_types type) - { return emitter::isValidImmShift(imm, EA_ATTR(genTypeSize(type))); }; - - GenTree* op2 = cmp->gtGetOp2(); + if (IsContainableBinaryOp(cmp, op2)) + { + MakeSrcContained(cmp, op2); + return; + } - if (op2->gtGetOp2()->IsCnsIntOrI() && - isValidImmForShift(op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), cmp->TypeGet())) + if (cmp->OperIs(GT_EQ, GT_NE) && IsContainableBinaryOp(cmp, op1)) { - op2->ClearContained(); - op2->gtGetOp1()->ClearContained(); + MakeSrcContained(cmp, op1); - MakeSrcContained(op2, op2->gtGetOp2()); - MakeSrcContained(cmp, op2); + std::swap(cmp->gtOp1, cmp->gtOp2); + return; } } -#endif // TARGET_ARM64 +#endif + +//#ifdef TARGET_ARM64 +// if (comp->opts.OptimizationEnabled() && cmp->gtGetOp2()->OperIs(GT_LSH, GT_RSH, GT_RSZ)) +// { +// auto isValidImmForShift = [](ssize_t imm, var_types type) +// { return emitter::isValidImmShift(imm, EA_ATTR(genTypeSize(type))); }; +// +// GenTree* op2 = cmp->gtGetOp2(); +// +// if (op2->gtGetOp2()->IsCnsIntOrI() && +// isValidImmForShift(op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), cmp->TypeGet()) && +// IsContainableBinaryOp(cmp, cmp->gtGetOp2())) +// { +// op2->ClearContained(); +// op2->gtGetOp1()->ClearContained(); +// +// MakeSrcContained(op2, op2->gtGetOp2()); +// MakeSrcContained(cmp, op2); +// } +// else if (cmp->OperIsCommutative() && IsContainableBinaryOp(cmp, cmp->gtGetOp1())) +// { +// } +// +// // if (node->OperIsCommutative() && IsContainableBinaryOp(node, op1)) +// //{ +// // if (op1->OperIs(GT_CAST)) +// // { +// // // We want to prefer the combined op here over containment of the cast op +// // op1->AsCast()->CastOp()->ClearContained(); +// // } +// // MakeSrcContained(node, op1); +// +// // std::swap(node->gtOp1, node->gtOp2); +// // return; +// //} +// } +//#endif // TARGET_ARM64 } #ifdef TARGET_ARM64 From 860c8dec1edd8397902b897615777d48d06066a2 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 10 Apr 2023 20:12:52 -0700 Subject: [PATCH 04/13] Remove commented code --- src/coreclr/jit/lowerarmarch.cpp | 37 -------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 58f5364fe89e12..6429a6be6f18cb 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -2288,43 +2288,6 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) } } #endif - -//#ifdef TARGET_ARM64 -// if (comp->opts.OptimizationEnabled() && cmp->gtGetOp2()->OperIs(GT_LSH, GT_RSH, GT_RSZ)) -// { -// auto isValidImmForShift = [](ssize_t imm, var_types type) -// { return emitter::isValidImmShift(imm, EA_ATTR(genTypeSize(type))); }; -// -// GenTree* op2 = cmp->gtGetOp2(); -// -// if (op2->gtGetOp2()->IsCnsIntOrI() && -// isValidImmForShift(op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), cmp->TypeGet()) && -// IsContainableBinaryOp(cmp, cmp->gtGetOp2())) -// { -// op2->ClearContained(); -// op2->gtGetOp1()->ClearContained(); -// -// MakeSrcContained(op2, op2->gtGetOp2()); -// MakeSrcContained(cmp, op2); -// } -// else if (cmp->OperIsCommutative() && IsContainableBinaryOp(cmp, cmp->gtGetOp1())) -// { -// } -// -// // if (node->OperIsCommutative() && IsContainableBinaryOp(node, op1)) -// //{ -// // if (op1->OperIs(GT_CAST)) -// // { -// // // We want to prefer the combined op here over containment of the cast op -// // op1->AsCast()->CastOp()->ClearContained(); -// // } -// // MakeSrcContained(node, op1); -// -// // std::swap(node->gtOp1, node->gtOp2); -// // return; -// //} -// } -//#endif // TARGET_ARM64 } #ifdef TARGET_ARM64 From 65592017952c7b29403172b7b6092e37243b9a9a Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 11 Apr 2023 13:38:56 -0700 Subject: [PATCH 05/13] Added more cases --- src/coreclr/jit/lowerarmarch.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6429a6be6f18cb..2b718dcfdde3a1 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -2271,7 +2271,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) return; #ifdef TARGET_ARM64 - if (comp->opts.OptimizationEnabled()) + if (comp->opts.OptimizationEnabled() && cmp->OperIsCompare()) { if (IsContainableBinaryOp(cmp, op2)) { @@ -2279,11 +2279,11 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) return; } - if (cmp->OperIs(GT_EQ, GT_NE) && IsContainableBinaryOp(cmp, op1)) + if (IsContainableBinaryOp(cmp, op1)) { MakeSrcContained(cmp, op1); - std::swap(cmp->gtOp1, cmp->gtOp2); + cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper)); return; } } From b72195d1b8c5e29219a5ff2d917ae10d56f0f3cc Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 11 Apr 2023 15:23:18 -0700 Subject: [PATCH 06/13] Initial work --- src/coreclr/jit/codegenarm64.cpp | 19 ++++++++++++++----- src/coreclr/jit/lowerarmarch.cpp | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 3bfe03b88ba8f3..7a287c8a6f5bf6 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4524,12 +4524,21 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) } else if (op2->isContained()) { - assert(op2->OperIs(GT_LSH, GT_RSH, GT_RSZ)); - assert(op2->gtGetOp2()->IsCnsIntOrI()); - assert(op2->gtGetOp2()->isContained()); + if (op2->OperIs(GT_NEG)) + { + assert(ins == INS_cmp); + ins = INS_cmn; + emit->emitIns_R_R(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum()); + } + else + { + assert(op2->OperIs(GT_LSH, GT_RSH, GT_RSZ)); + assert(op2->gtGetOp2()->IsCnsIntOrI()); + assert(op2->gtGetOp2()->isContained()); - emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(), - op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(op2->gtOper)); + emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(), + op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(op2->gtOper)); + } } else { diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 2b718dcfdde3a1..93b8f70eb3f0fd 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -282,6 +282,19 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co return false; } + if (childNode->OperIs(GT_NEG)) + { + if (parentNode->OperIs(GT_CMP)) + { + if (IsInvariantInRange(childNode, parentNode)) + { + return true; + } + } + + return false; + } + if (childNode->OperIs(GT_CAST)) { // Find "a op cast(b)" @@ -2271,7 +2284,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) return; #ifdef TARGET_ARM64 - if (comp->opts.OptimizationEnabled() && cmp->OperIsCompare()) + if (comp->opts.OptimizationEnabled() && (cmp->OperIsCompare() || cmp->OperIs(GT_CMP))) { if (IsContainableBinaryOp(cmp, op2)) { From 1d191342a66c163f6e67895eed157f0e9ef8aace Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 11 Apr 2023 16:25:20 -0700 Subject: [PATCH 07/13] Emitting cmn --- src/coreclr/jit/lower.cpp | 10 ++++++++++ src/coreclr/jit/lowerarmarch.cpp | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 408821ba32dcea..06e8d0cc171dff 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3851,6 +3851,16 @@ bool Lowering::TryLowerConditionToFlagsNode(GenTree* parent, GenTree* condition, BlockRange().Remove(relop); BlockRange().Remove(relopOp2); } +#ifdef TARGET_ARM64 + else if (optimizing && relop->OperIs(GT_EQ, GT_NE, GT_LT, GT_LE, GT_GE, GT_GT) && + (IsContainableBinaryOp(relop, relop->gtGetOp1()) || IsContainableBinaryOp(relop, relop->gtGetOp2()))) + { + ContainCheckCompare(relop); + relop->SetOper(GT_CMP); + relop->gtType = TYP_VOID; + relop->gtFlags |= GTF_SET_FLAGS; + } +#endif // TARGET_ARM64 else { relop->gtType = TYP_VOID; diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 93b8f70eb3f0fd..6fcb1515e2b468 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -284,7 +284,7 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co if (childNode->OperIs(GT_NEG)) { - if (parentNode->OperIs(GT_CMP)) + if (parentNode->OperIs(GT_CMP) || parentNode->OperIsCompare()) { if (IsInvariantInRange(childNode, parentNode)) { @@ -2296,7 +2296,10 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) { MakeSrcContained(cmp, op1); std::swap(cmp->gtOp1, cmp->gtOp2); - cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper)); + if (cmp->OperIsCompare()) + { + cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper)); + } return; } } From e5f0ab3ca78fbcea82dfd1277f2bb5e718f5c71c Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 17 Apr 2023 16:27:23 -0700 Subject: [PATCH 08/13] Handle shift-version of neg and cmn --- src/coreclr/jit/codegenarm64.cpp | 94 +++++++++++++++++++++++++------- src/coreclr/jit/lowerarmarch.cpp | 20 ++++++- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 2c5b71f3b7bb25..23639d3684e27a 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3359,13 +3359,40 @@ void CodeGen::genCodeForNegNot(GenTree* tree) // The src must be a register. if (tree->OperIs(GT_NEG) && operand->isContained()) { - ins = INS_mneg; - GenTree* op1 = tree->gtGetOp1(); - GenTree* a = op1->gtGetOp1(); - GenTree* b = op1->gtGetOp2(); - genConsumeRegs(op1); - assert(op1->OperGet() == GT_MUL); - GetEmitter()->emitIns_R_R_R(ins, emitActualTypeSize(tree), targetReg, a->GetRegNum(), b->GetRegNum()); + genTreeOps oper = operand->OperGet(); + switch (oper) + { + case GT_MUL: + { + ins = INS_mneg; + GenTree* op1 = tree->gtGetOp1(); + GenTree* a = op1->gtGetOp1(); + GenTree* b = op1->gtGetOp2(); + genConsumeRegs(op1); + GetEmitter()->emitIns_R_R_R(ins, emitActualTypeSize(tree), targetReg, a->GetRegNum(), b->GetRegNum()); + } + break; + + case GT_LSH: + case GT_RSH: + case GT_RSZ: + { + assert(ins == INS_neg || ins == INS_negs); + assert(operand->gtGetOp2()->IsCnsIntOrI()); + assert(operand->gtGetOp2()->isContained()); + + GenTree* op1 = tree->gtGetOp1(); + GenTree* a = op1->gtGetOp1(); + GenTree* b = op1->gtGetOp2(); + genConsumeRegs(op1); + GetEmitter()->emitIns_R_R_I(ins, emitActualTypeSize(tree), targetReg, a->GetRegNum(), + b->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(oper)); + } + break; + + default: + unreached(); + } } else { @@ -4524,20 +4551,49 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) } else if (op2->isContained()) { - if (op2->OperIs(GT_NEG)) - { - assert(ins == INS_cmp); - ins = INS_cmn; - emit->emitIns_R_R(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum()); - } - else + genTreeOps oper = op2->OperGet(); + switch (oper) { - assert(op2->OperIs(GT_LSH, GT_RSH, GT_RSZ)); - assert(op2->gtGetOp2()->IsCnsIntOrI()); - assert(op2->gtGetOp2()->isContained()); + case GT_NEG: + assert(ins == INS_cmp); - emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(), - op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(op2->gtOper)); + ins = INS_cmn; + oper = op2->gtGetOp1()->OperGet(); + switch (oper) + { + case GT_LSH: + case GT_RSH: + case GT_RSZ: + { + GenTree* shiftOp1 = op2->gtGetOp1()->gtGetOp1(); + GenTree* shiftOp2 = op2->gtGetOp1()->gtGetOp2(); + + assert(shiftOp2->IsCnsIntOrI()); + assert(shiftOp2->isContained()); + + emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), shiftOp1->GetRegNum(), + shiftOp2->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(oper)); + } + break; + + default: + emit->emitIns_R_R(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum()); + break; + } + break; + + case GT_LSH: + case GT_RSH: + case GT_RSZ: + assert(op2->gtGetOp2()->IsCnsIntOrI()); + assert(op2->gtGetOp2()->isContained()); + + emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(), + op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(oper)); + break; + + default: + unreached(); } } else diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 2c590e31644e0d..6fb116f53e2d12 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -158,8 +158,18 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const // bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) const { +#ifdef DEBUG // The node we're checking should be one of the two child nodes - assert((parentNode->gtGetOp1() == childNode) || (parentNode->gtGetOp2() == childNode)); + if (parentNode->OperIsBinary()) + { + assert((parentNode->gtGetOp1() == childNode) || (parentNode->gtGetOp2() == childNode)); + } + else + { + assert(parentNode->OperIsUnary()); + assert((parentNode->gtGetOp1() == childNode)); + } +#endif // We cannot contain if the parent node // * is contained @@ -173,7 +183,7 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co if (!varTypeIsIntegral(parentNode)) return false; - if (parentNode->gtGetOp1()->isContained() || parentNode->gtGetOp2()->isContained()) + if (parentNode->gtGetOp1()->isContained() || (parentNode->OperIsBinary() && parentNode->gtGetOp2()->isContained())) return false; if (parentNode->OperMayOverflow() && parentNode->gtOverflow()) @@ -252,7 +262,7 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co return false; } - if (parentNode->OperIs(GT_ADD, GT_SUB, GT_AND)) + if (parentNode->OperIs(GT_ADD, GT_SUB, GT_AND, GT_NEG)) { // These operations can still report flags @@ -2540,6 +2550,10 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg) MakeSrcContained(neg, childNode); } } + else if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && IsContainableBinaryOp(neg, childNode)) + { + MakeSrcContained(neg, childNode); + } } //---------------------------------------------------------------------------------------------- From fc27a864c0cd48aa22c0c8f30a4701413996c7d6 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 17 Apr 2023 16:29:25 -0700 Subject: [PATCH 09/13] Add optimization check --- src/coreclr/jit/lowerarmarch.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6fb116f53e2d12..8f38cc208cdce2 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -2550,7 +2550,8 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg) MakeSrcContained(neg, childNode); } } - else if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && IsContainableBinaryOp(neg, childNode)) + else if (comp->opts.OptimizationEnabled() && childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && + IsContainableBinaryOp(neg, childNode)) { MakeSrcContained(neg, childNode); } From 670088d2934e1b57f54f64bc185170bf00637038 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 17 Apr 2023 18:12:01 -0700 Subject: [PATCH 10/13] Check for set flags --- src/coreclr/jit/lowerarmarch.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 8f38cc208cdce2..40ad0d2702759c 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -294,6 +294,12 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co if (childNode->OperIs(GT_NEG)) { + if ((parentNode->gtFlags & GTF_SET_FLAGS) != 0) + { + // Cannot contain if the parent operation needs to set flags + return false; + } + if (parentNode->OperIs(GT_CMP) || parentNode->OperIsCompare()) { if (IsInvariantInRange(childNode, parentNode)) From db741849f6cf7a14653e76ed24e7bb5ebd77df2f Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 18 Apr 2023 13:11:04 -0700 Subject: [PATCH 11/13] Added extra asserts --- src/coreclr/jit/codegenarm64.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 23639d3684e27a..686914a3460b28 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4568,6 +4568,7 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) GenTree* shiftOp1 = op2->gtGetOp1()->gtGetOp1(); GenTree* shiftOp2 = op2->gtGetOp1()->gtGetOp2(); + assert(op2->gtGetOp1()->isContained()); assert(shiftOp2->IsCnsIntOrI()); assert(shiftOp2->isContained()); @@ -4577,6 +4578,8 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) break; default: + assert(!op2->gtGetOp1()->isContained()); + emit->emitIns_R_R(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum()); break; } From 993deabb3421085399f6b4037ec9a11749b52060 Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 18 Apr 2023 14:34:07 -0700 Subject: [PATCH 12/13] Renamed IsContainableBinaryOp to IsContainableUnaryOrBinaryOp. Fixed an assert. --- src/coreclr/jit/lower.cpp | 3 ++- src/coreclr/jit/lower.h | 2 +- src/coreclr/jit/lowerarmarch.cpp | 22 ++++++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 6355fd8b947fc8..3e4b35947019b2 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3918,7 +3918,8 @@ bool Lowering::TryLowerConditionToFlagsNode(GenTree* parent, GenTree* condition, } #ifdef TARGET_ARM64 else if (optimizing && relop->OperIs(GT_EQ, GT_NE, GT_LT, GT_LE, GT_GE, GT_GT) && - (IsContainableBinaryOp(relop, relop->gtGetOp1()) || IsContainableBinaryOp(relop, relop->gtGetOp2()))) + (IsContainableUnaryOrBinaryOp(relop, relop->gtGetOp1()) || + IsContainableUnaryOrBinaryOp(relop, relop->gtGetOp2()))) { ContainCheckCompare(relop); relop->SetOper(GT_CMP); diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 8b7cae89422bbb..512c6ea461bdb4 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -474,7 +474,7 @@ class Lowering final : public Phase } #ifdef TARGET_ARM64 - bool IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) const; + bool IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childNode) const; #endif // TARGET_ARM64 #if defined(FEATURE_HW_INTRINSICS) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 40ad0d2702759c..fcaae618cd37bd 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -148,7 +148,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const #ifdef TARGET_ARM64 //------------------------------------------------------------------------ -// IsContainableBinaryOp: Is the child node a binary op that is containable from the parent node? +// IsContainableUnaryOrBinaryOp: Is the child node a unary/binary op that is containable from the parent node? // // Return Value: // True if the child node can be contained. @@ -156,7 +156,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const // Notes: // This can handle the decision to emit 'madd' or 'msub'. // -bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) const +bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childNode) const { #ifdef DEBUG // The node we're checking should be one of the two child nodes @@ -169,7 +169,7 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co assert(parentNode->OperIsUnary()); assert((parentNode->gtGetOp1() == childNode)); } -#endif +#endif // DEBUG // We cannot contain if the parent node // * is contained @@ -294,6 +294,12 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co if (childNode->OperIs(GT_NEG)) { + if (childNode->gtGetOp1()->isContained()) + { + // Cannot contain if the childs op1 is already contained + return false; + } + if ((parentNode->gtFlags & GTF_SET_FLAGS) != 0) { // Cannot contain if the parent operation needs to set flags @@ -2079,7 +2085,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) #ifdef TARGET_ARM64 if (comp->opts.OptimizationEnabled()) { - if (IsContainableBinaryOp(node, op2)) + if (IsContainableUnaryOrBinaryOp(node, op2)) { if (op2->OperIs(GT_CAST)) { @@ -2091,7 +2097,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) return; } - if (node->OperIsCommutative() && IsContainableBinaryOp(node, op1)) + if (node->OperIsCommutative() && IsContainableUnaryOrBinaryOp(node, op1)) { if (op1->OperIs(GT_CAST)) { @@ -2313,13 +2319,13 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp) #ifdef TARGET_ARM64 if (comp->opts.OptimizationEnabled() && (cmp->OperIsCompare() || cmp->OperIs(GT_CMP))) { - if (IsContainableBinaryOp(cmp, op2)) + if (IsContainableUnaryOrBinaryOp(cmp, op2)) { MakeSrcContained(cmp, op2); return; } - if (IsContainableBinaryOp(cmp, op1)) + if (IsContainableUnaryOrBinaryOp(cmp, op1)) { MakeSrcContained(cmp, op1); std::swap(cmp->gtOp1, cmp->gtOp2); @@ -2557,7 +2563,7 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg) } } else if (comp->opts.OptimizationEnabled() && childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && - IsContainableBinaryOp(neg, childNode)) + IsContainableUnaryOrBinaryOp(neg, childNode)) { MakeSrcContained(neg, childNode); } From ab86220b93b178d5255836c4feb8bdd4bad5e0cf Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 19 Apr 2023 17:57:31 -0700 Subject: [PATCH 13/13] Remove optimizing code from TryLowerConditionToFlagsNode. Added additional check in containing NEG for a CMP or compare op. --- src/coreclr/jit/lower.cpp | 17 +++++------------ src/coreclr/jit/lowerarmarch.cpp | 3 ++- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 3e4b35947019b2..0e8e040d1a40d4 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3777,7 +3777,7 @@ GenTree* Lowering::LowerJTrue(GenTreeOp* jtrue) jtrue->AsCC()->gtCondition = condCode; } - JITDUMP("Result:\n"); + JITDUMP("Lowering JTRUE Result:\n"); DISPTREERANGE(BlockRange(), jtrue); JITDUMP("\n"); @@ -3878,6 +3878,10 @@ GenTree* Lowering::LowerSelect(GenTreeConditional* select) // bool Lowering::TryLowerConditionToFlagsNode(GenTree* parent, GenTree* condition, GenCondition* cond) { + JITDUMP("Lowering condition:\n"); + DISPTREERANGE(BlockRange(), condition); + JITDUMP("\n"); + if (condition->OperIsCompare()) { if (!IsInvariantInRange(condition, parent)) @@ -3916,17 +3920,6 @@ bool Lowering::TryLowerConditionToFlagsNode(GenTree* parent, GenTree* condition, BlockRange().Remove(relop); BlockRange().Remove(relopOp2); } -#ifdef TARGET_ARM64 - else if (optimizing && relop->OperIs(GT_EQ, GT_NE, GT_LT, GT_LE, GT_GE, GT_GT) && - (IsContainableUnaryOrBinaryOp(relop, relop->gtGetOp1()) || - IsContainableUnaryOrBinaryOp(relop, relop->gtGetOp2()))) - { - ContainCheckCompare(relop); - relop->SetOper(GT_CMP); - relop->gtType = TYP_VOID; - relop->gtFlags |= GTF_SET_FLAGS; - } -#endif // TARGET_ARM64 else { relop->gtType = TYP_VOID; diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index fcaae618cd37bd..5f0cdb87ffb35c 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -294,7 +294,8 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN if (childNode->OperIs(GT_NEG)) { - if (childNode->gtGetOp1()->isContained()) + // If we have a contained LSH, RSH or RSZ, we can still contain NEG if the parent is a CMP or comparison op. + if (childNode->gtGetOp1()->isContained() && !childNode->gtGetOp1()->OperIs(GT_LSH, GT_RSH, GT_RSZ)) { // Cannot contain if the childs op1 is already contained return false;