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

Add support for containing RotateLeft/RotateRight on Arm64 #101030

Merged
merged 5 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2655,6 +2655,48 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
genProduceReg(tree);
return;
}
else if (op2->OperIs(GT_ROR) && op2->isContained())
{
assert(varTypeIsIntegral(tree));

GenTree* a = op1;
GenTree* b = op2->gtGetOp1();
GenTree* c = op2->gtGetOp2();

// The rotate amount needs to be contained as well
assert(c->isContained() && c->IsCnsIntOrI());

instruction ins = genGetInsForOper(tree->OperGet(), targetType);
insOpts opt = INS_OPTS_NONE;

if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
{
// A subset of operations can still set flags

switch (oper)
{
case GT_AND:
{
ins = INS_ands;
break;
}

default:
{
noway_assert(!"Unexpected BinaryOp with GTF_SET_FLAGS set");
}
}
}

assert(op2->OperIs(GT_ROR));
opt = INS_OPTS_ROR;

emit->emitIns_R_R_R_I(ins, emitActualTypeSize(tree), targetReg, a->GetRegNum(), b->GetRegNum(),
c->AsIntConCommon()->IconValue(), opt);

genProduceReg(tree);
return;
}
else if (op2->OperIs(GT_CAST) && op2->isContained())
{
assert(varTypeIsIntegral(tree));
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ void CodeGen::genConsumeRegs(GenTree* tree)
}
#endif // FEATURE_HW_INTRINSICS
#endif // TARGET_XARCH
else if (tree->OperIs(GT_BITCAST, GT_NEG, GT_CAST, GT_LSH, GT_RSH, GT_RSZ, GT_BSWAP, GT_BSWAP16))
else if (tree->OperIs(GT_BITCAST, GT_NEG, GT_CAST, GT_LSH, GT_RSH, GT_RSZ, GT_ROR, GT_BSWAP, GT_BSWAP16))
{
genConsumeRegs(tree->gtGetOp1());
}
Expand Down
68 changes: 68 additions & 0 deletions src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,74 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN
return false;
}

if (childNode->OperIs(GT_ROL, GT_ROR))
{
// Find "a op (b rotate cns)"

if (childNode->gtGetOp1()->isContained())
{
// Cannot contain if the childs op1 is already contained
return false;
}

GenTree* rotateAmountNode = childNode->gtGetOp2();

if (!rotateAmountNode->IsCnsIntOrI())
{
// Cannot contain if the childs op2 is not a constant
return false;
}

const ssize_t wrapAmount = (static_cast<ssize_t>(genTypeSize(parentNode)) * BITS_PER_BYTE);
assert((wrapAmount == 32) || (wrapAmount == 64));

// Rotation is circular, so normalize to [0, wrapAmount - 1]
ssize_t rotateAmount = rotateAmountNode->AsIntCon()->IconValue() % wrapAmount;
assert((rotateAmount >= 0) && (rotateAmount <= (wrapAmount - 1)));

if (childNode->OperIs(GT_ROL))
{
// The actual instructions only encode rotate right but
// since rotating left by 1 is equivalen to rotating
// right by (rotateAmount - 1), we can fix things here.

childNode->SetOper(GT_ROR);
rotateAmount = wrapAmount - rotateAmount;
}

rotateAmountNode->AsIntCon()->SetIconValue(rotateAmount);
assert(childNode->OperIs(GT_ROR));

if (parentNode->OperIs(GT_AND))
{
// These operations can still report flags

if (IsInvariantInRange(childNode, parentNode))
{
assert(rotateAmountNode->isContained());
return true;
}
}

if ((parentNode->gtFlags & GTF_SET_FLAGS) != 0)
{
// Cannot contain if the parent operation needs to set flags
return false;
}

if (parentNode->OperIs(GT_OR, GT_XOR))
{
if (IsInvariantInRange(childNode, parentNode))
{
assert(rotateAmountNode->isContained());
return true;
}
}

// TODO: Handle BIC/BICS, EON, MVN, ORN, TST
return false;
}

if (childNode->OperIs(GT_NEG))
{
// If we have a contained LSH, RSH or RSZ, we can still contain NEG if the parent is a EQ or NE.
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/lsrabuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3511,11 +3511,11 @@ int LinearScan::BuildOperandUses(GenTree* node, regMaskTP candidates)
// ANDs may be contained in a chain.
return BuildBinaryUses(node->AsOp(), candidates);
}
if (node->OperIs(GT_NEG, GT_CAST, GT_LSH, GT_RSH, GT_RSZ))
if (node->OperIs(GT_NEG, GT_CAST, GT_LSH, GT_RSH, GT_RSZ, GT_ROR))
{
// NEG can be contained for mneg on arm64
// CAST and LSH for ADD with sign/zero extension
// LSH, RSH, and RSZ for various "shifted register" instructions on arm64
// LSH, RSH, RSZ, and ROR for various "shifted register" instructions on arm64
return BuildOperandUses(node->gtGetOp1(), candidates);
}
#endif
Expand Down
Loading