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

[arm64] JIT: Recognize sbfiz/ubfiz idioms #61045

Merged
merged 14 commits into from
Nov 4, 2021
1 change: 1 addition & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCodeForJumpTrue(GenTreeOp* jtrue);
#ifdef TARGET_ARM64
void genCodeForJumpCompare(GenTreeOp* tree);
void genCodeForBfiz(GenTreeOp* tree);
#endif // TARGET_ARM64

#if defined(FEATURE_EH_FUNCLETS)
Expand Down
26 changes: 26 additions & 0 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9564,4 +9564,30 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind)
}
}

//------------------------------------------------------------------------
// genCodeForBfiz: Generates the code sequence for a GenTree node that
// represents a bitfield insert in zero with sign/zero extension.
//
// Arguments:
// tree - the bitfield insert in zero node.
//
void CodeGen::genCodeForBfiz(GenTreeOp* tree)
{
assert(tree->OperIs(GT_BFIZ));

emitAttr size = emitActualTypeSize(tree);
unsigned shiftBy = (unsigned)tree->gtGetOp2()->AsIntCon()->IconValue();
unsigned shiftByImm = shiftBy & (emitter::getBitWidth(size) - 1);
GenTreeCast* cast = tree->gtGetOp1()->AsCast();
GenTree* castOp = cast->CastOp();

genConsumeRegs(castOp);
unsigned srcBits = varTypeIsSmall(cast->CastToType()) ? genTypeSize(cast->CastToType()) * BITS_PER_BYTE
: genTypeSize(castOp) * BITS_PER_BYTE;
const bool isUnsigned = cast->IsUnsigned() || varTypeIsUnsigned(cast->CastToType());
GetEmitter()->emitIns_R_R_I_I(isUnsigned ? INS_ubfiz : INS_sbfiz, size, tree->GetRegNum(), castOp->GetRegNum(),
(int)shiftByImm, (int)srcBits);
genProduceReg(tree);
}

#endif // TARGET_ARM64
12 changes: 8 additions & 4 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
case GT_SWAP:
genCodeForSwap(treeNode->AsOp());
break;

case GT_BFIZ:
genCodeForBfiz(treeNode->AsOp());
break;
#endif // TARGET_ARM64

case GT_JMP:
Expand Down Expand Up @@ -1614,23 +1618,23 @@ void CodeGen::genCodeForShift(GenTree* tree)
genTreeOps oper = tree->OperGet();
instruction ins = genGetInsForOper(oper, targetType);
emitAttr size = emitActualTypeSize(tree);
regNumber dstReg = tree->GetRegNum();

assert(tree->GetRegNum() != REG_NA);
assert(dstReg != REG_NA);

genConsumeOperands(tree->AsOp());

GenTree* operand = tree->gtGetOp1();
GenTree* shiftBy = tree->gtGetOp2();
if (!shiftBy->IsCnsIntOrI())
{
GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftBy->GetRegNum());
GetEmitter()->emitIns_R_R_R(ins, size, dstReg, operand->GetRegNum(), shiftBy->GetRegNum());
}
else
{
unsigned immWidth = emitter::getBitWidth(size); // For ARM64, immWidth will be set to 32 or 64
unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal & (immWidth - 1);

GetEmitter()->emitIns_R_R_I(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftByImm);
GetEmitter()->emitIns_R_R_I(ins, size, dstReg, operand->GetRegNum(), shiftByImm);
}

genProduceReg(tree);
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/gtlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ GTNODE(PHI_ARG , GenTreePhiArg ,0,(GTK_LEAF|GTK_LOCAL)) // phi

GTNODE(JMPTABLE , GenTree ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Generates the jump table for switches
GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct
#ifdef TARGET_ARM64
GTNODE(BFIZ, GenTreeBfiz ,0, GTK_BINOP) // Bitfield Insert in Zero
#endif

//-----------------------------------------------------------------------------
// Nodes used only within the code generator:
Expand Down
29 changes: 29 additions & 0 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5753,6 +5753,35 @@ void Lowering::LowerShift(GenTreeOp* shift)
shift->gtOp2->ClearContained();
}
ContainCheckShiftRotate(shift);

#ifdef TARGET_ARM64
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
// Try to recognize ubfiz/sbfiz idiom in LSH(CAST(X), CNS) tree
if (comp->opts.OptimizationEnabled() && shift->OperIs(GT_LSH) && shift->gtGetOp1()->OperIs(GT_CAST) &&
shift->gtGetOp2()->IsCnsIntOrI() && !shift->isContained())
{
GenTreeIntCon* cns = shift->gtGetOp2()->AsIntCon();
GenTreeCast* cast = shift->gtGetOp1()->AsCast();

if (!cast->isContained() && !cast->IsRegOptional() && !cast->gtOverflow() &&
// Smaller CastOp is most likely an IND(X) node which is lowered to a zero-extend load
cast->CastOp()->TypeIs(TYP_LONG, TYP_INT))
{
// Cast is either "TYP_LONG <- TYP_INT" or "TYP_INT <- %SMALL_INT% <- TYP_INT" (signed or unsigned)
unsigned dstBits = genTypeSize(cast) * BITS_PER_BYTE;
unsigned srcBits = varTypeIsSmall(cast->CastToType()) ? genTypeSize(cast->CastToType()) * BITS_PER_BYTE
: genTypeSize(cast->CastOp()) * BITS_PER_BYTE;
assert(!cast->CastOp()->isContained());

// It has to be an upcast and CNS must be in [1..srcBits) range
if ((srcBits < dstBits) && ((UINT32)cns->IconValue() < srcBits))
{
JITDUMP("Recognized ubfix/sbfix pattern in LSH(CAST, CNS). Changing op to GT_BFIZ");
shift->ChangeOper(GT_BFIZ);
MakeSrcContained(shift, cast);
}
}
}
#endif
}

void Lowering::WidenSIMD12IfNecessary(GenTreeLclVarCommon* node)
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/lsraarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ int LinearScan::BuildNode(GenTree* tree)
BuildDef(tree);
break;

case GT_BFIZ:
assert(tree->gtGetOp1()->OperIs(GT_CAST));
srcCount = BuildOperandUses(tree->gtGetOp1()->gtGetOp1());
BuildDef(tree);
break;

case GT_RETURNTRAP:
// this just turns into a compare of its child with an int
// + a conditional call
Expand Down
Loading