Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JIT] ARM64 - Combine 'neg' and 'cmp' to 'cmn' #84667

Merged
merged 15 commits into from
Apr 21, 2023
92 changes: 80 additions & 12 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -4524,12 +4551,53 @@ 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());
genTreeOps oper = op2->OperGet();
switch (oper)
{
case GT_NEG:
assert(ins == INS_cmp);

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(op2->gtGetOp1()->isContained());
assert(shiftOp2->IsCnsIntOrI());
assert(shiftOp2->isContained());

emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), shiftOp1->GetRegNum(),
shiftOp2->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(oper));
}
break;

default:
assert(!op2->gtGetOp1()->isContained());

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;

emit->emitIns_R_R_I(ins, cmpSize, op1->GetRegNum(), op2->gtGetOp1()->GetRegNum(),
op2->gtGetOp2()->AsIntConCommon()->IntegralValue(), ShiftOpToInsOpts(op2->gtOper));
default:
unreached();
}
}
else
{
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,17 @@ 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
TIHan marked this conversation as resolved.
Show resolved Hide resolved
else
{
relop->gtType = TYP_VOID;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
65 changes: 54 additions & 11 deletions src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,28 @@ 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.
//
// 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
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 // DEBUG

// We cannot contain if the parent node
// * is contained
Expand All @@ -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())
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -282,6 +292,31 @@ bool Lowering::IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) co
return false;
}

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
return false;
}

if (parentNode->OperIs(GT_CMP) || parentNode->OperIsCompare())
{
if (IsInvariantInRange(childNode, parentNode))
{
return true;
}
}

return false;
}

if (childNode->OperIs(GT_CAST))
{
// Find "a op cast(b)"
Expand Down Expand Up @@ -2050,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))
{
Expand All @@ -2062,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))
{
Expand Down Expand Up @@ -2282,19 +2317,22 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
}

#ifdef TARGET_ARM64
if (comp->opts.OptimizationEnabled() && cmp->OperIsCompare())
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);
cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper));
if (cmp->OperIsCompare())
{
cmp->ChangeOper(cmp->SwapRelop(cmp->gtOper));
}
return;
}
}
Expand Down Expand Up @@ -2524,6 +2562,11 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg)
MakeSrcContained(neg, childNode);
}
}
else if (comp->opts.OptimizationEnabled() && childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) &&
IsContainableUnaryOrBinaryOp(neg, childNode))
{
MakeSrcContained(neg, childNode);
}
}

//----------------------------------------------------------------------------------------------
Expand Down