Skip to content

Commit

Permalink
Add support for containing RotateLeft/RotateRight on Arm64 (dotnet#10…
Browse files Browse the repository at this point in the history
…1030)

* Add support for containing RotateLeft/RotateRight on Arm64

* Fix a variable name

* Applying formatting patch

* Ensure lsrabuild handles contained GT_ROR

* Ensure codegenlinear can consume contained GT_ROR
  • Loading branch information
tannergooding authored and matouskozak committed Apr 30, 2024
1 parent 145f333 commit bae5a06
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
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

0 comments on commit bae5a06

Please sign in to comment.