Skip to content
9 changes: 3 additions & 6 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,16 +708,13 @@ void CodeGen::genBuildRegPairsStack(regMaskTP regsMask, ArrayStack<RegPair>* reg

while (regsMask != RBM_NONE)
{
regMaskTP reg1Mask = genFindLowestBit(regsMask);
regNumber reg1 = genRegNumFromMask(reg1Mask);
regsMask &= ~reg1Mask;
regNumber reg1 = genFirstRegNumFromMaskAndToggle(regsMask);
regsCount -= 1;

bool isPairSave = false;
if (regsCount > 0)
{
regMaskTP reg2Mask = genFindLowestBit(regsMask);
regNumber reg2 = genRegNumFromMask(reg2Mask);
regNumber reg2 = genFirstRegNumFromMask(regsMask);
if (reg2 == REG_NEXT(reg1))
{
// The JIT doesn't allow saving pair (R28,FP), even though the
Expand All @@ -733,7 +730,7 @@ void CodeGen::genBuildRegPairsStack(regMaskTP regsMask, ArrayStack<RegPair>* reg
{
isPairSave = true;

regsMask &= ~reg2Mask;
regsMask &= ~genRegMask(reg2);
Comment thread
kunalspathak marked this conversation as resolved.
Outdated
regsCount -= 1;

regStack->Push(RegPair(reg1, reg2));
Expand Down
44 changes: 44 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,50 @@ inline regNumber genRegNumFromMask(regMaskTP mask)
return regNum;
}

//------------------------------------------------------------------------------
// genFirstRegNumFromMaskAndToggle : Maps first bit set in the register mask to a
// register number and also toggle the bit in the `mask`.
// Arguments:
// mask - the register mask
//
// Return Value:
// The number of the first register contained in the mask and updates the `mask` to toggle
// the bit.
//

inline regNumber genFirstRegNumFromMaskAndToggle(regMaskTP& mask)
{
assert(mask != 0); // Must have one bit set, so can't have a mask of zero

/* Convert the mask to a register number */

regNumber regNum = (regNumber)BitOperations::BitScanForward(mask);
mask ^= genRegMask(regNum);

return regNum;
}

//------------------------------------------------------------------------------
// genFirstRegNumFromMask : Maps first bit set in the register mask to a register number.
//
// Arguments:
// mask - the register mask
//
// Return Value:
// The number of the first register contained in the mask.
//

inline regNumber genFirstRegNumFromMask(regMaskTP mask)
{
assert(mask != 0); // Must have one bit set, so can't have a mask of zero

/* Convert the mask to a register number */

regNumber regNum = (regNumber)BitOperations::BitScanForward(mask);

return regNum;
}

/*****************************************************************************
*
* Return the size in bytes of the given type.
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25802,9 +25802,9 @@ regNumber GenTree::ExtractTempReg(regMaskTP mask /* = (regMaskTP)-1 */)
{
regMaskTP availableSet = gtRsvdRegs & mask;
assert(genCountBits(availableSet) >= 1);
regMaskTP tempRegMask = genFindLowestBit(availableSet);
gtRsvdRegs &= ~tempRegMask;
return genRegNumFromMask(tempRegMask);
regNumber tempReg = genFirstRegNumFromMask(availableSet);
gtRsvdRegs ^= genRegMask(tempReg);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually faster than the previous code? Since it needs to do either a left shift (on amd64) or memory lookup (non-amd64). The same question applies to all the places where you introduced genRegMask.

It seems like you're saying b = genRegMask(...) + a ^= b is faster than a &= ~b?

The genFirstRegNumFromMaskAndToggle cases seem like a clear win, but I'm not as sure about these.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a ^= (1 << …) is specially recognized and transformed into btc on xarch. There is sometimes special optimizations possible on Arm64 as well, but it’s worst case the same number of instructions and execution cost (but often slightly shorter)

return tempReg;
}

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