Skip to content
Merged
6 changes: 6 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,12 @@ class CodeGen final : public CodeGenInterface
#endif // defined(TARGET_XARCH)

#ifdef TARGET_ARM64
void genEmbeddedMaskedHWIntrinsic(GenTreeHWIntrinsic* cndSelNode, regNumber targetReg);

#ifdef DEBUG
void checkRMWRegisters(const HWIntrinsic intrin, regNumber targetReg);
#endif

class HWIntrinsicImmOpHelper final
{
public:
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/jit/emitarm64sve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19318,7 +19318,10 @@ void emitter::emitInsPairSanityCheck(instrDesc* firstId, instrDesc* secondId)
assert(firstId->idReg2() == secondId->idReg2());

// "predicated using the same governing predicate register and source element size as this instruction."
assert(firstId->idInsOpt() == secondId->idInsOpt());
emitAttr dstSize1 = optGetSveElemsize(firstId->idInsOpt());
emitAttr dstSize2 = insOptsScalableStandard(secondId->idInsOpt()) ? optGetSveElemsize(secondId->idInsOpt())
: optGetDstsize(secondId->idInsOpt());
assert(dstSize1 == dstSize2);
}

// The following instructions cannot use predicated movprfx, else the behaviour will be unpredictable.
Expand Down
1,298 changes: 602 additions & 696 deletions src/coreclr/jit/hwintrinsiccodegenarm64.cpp

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3965,23 +3965,26 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
}

// Handle op3
if (op3->IsVectorZero() && op1->IsTrueMask(node->GetSimdBaseType()) && op2->IsEmbMaskOp())
if (op3->IsVectorZero() && op2->IsEmbMaskOp())
{
// When we are merging with zero, we can specialize
// and avoid instantiating the vector constant.
// Do this only if op1 was AllTrueMask
// When we are merging with zero, we can specialize and avoid instantiating the vector constant.
switch (op2->AsHWIntrinsic()->GetHWIntrinsicId())
{
case NI_Sve2_AddPairwise:
case NI_Sve2_MaxNumberPairwise:
case NI_Sve2_MaxPairwise:
case NI_Sve2_MinNumberPairwise:
case NI_Sve2_MinPairwise:
// This is an edge case where these instructions have unpredictable behaviour when
// using predicated movprfx, so the unpredicated variant must be used here. This
// prevents us from performing this optimization as we will need the constant vector
// for masking the result.
break;
case NI_Sve2_ConvertToDoubleOdd:
case NI_Sve2_ConvertToSingleOdd:
case NI_Sve2_ConvertToSingleOddRoundToOdd:
// This is an edge case where these instructions do not support predicated or any movprfx.
if (!op1->IsTrueMask(node->GetSimdBaseType()))
{
// When op1 is not all-true, we will need the constant vector for masking the result.
break;
}
FALLTHROUGH;

default:
MakeSrcContained(node, op3);
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/jit/lsra.h
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,7 @@ class LinearScan : public RegAllocInterface
FORCEINLINE RefPosition* getNextConsecutiveRefPosition(RefPosition* refPosition);
FORCEINLINE regNumber getNextFPRegWraparound(regNumber reg);
SingleTypeRegSet getOperandCandidates(GenTreeHWIntrinsic* intrinsicTree, HWIntrinsic intrin, size_t opNum);
GenTree* getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, bool embedded = false);
GenTree* getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, GenTreeHWIntrinsic* user = nullptr);
GenTree* getVectorAddrOperand(GenTreeHWIntrinsic* intrinsicTree);
GenTree* getConsecutiveRegistersOperand(const HWIntrinsic intrin, bool* destIsConsecutive);
GenTreeHWIntrinsic* getEmbeddedMaskOperand(const HWIntrinsic intrin);
Expand Down Expand Up @@ -2078,6 +2078,9 @@ class LinearScan : public RegAllocInterface
int BuildConsecutiveRegistersForUse(GenTree* treeNode, GenTree* rmwNode = nullptr);
void BuildConsecutiveRegistersForDef(GenTree* treeNode, int fieldCount);
void BuildHWIntrinsicImmediate(GenTreeHWIntrinsic* intrinsicTree, const HWIntrinsic intrin);
void BuildHWIntrinsicTempRegs(GenTreeHWIntrinsic* intrinsicTree,
const HWIntrinsic intrin,
GenTreeHWIntrinsic* embeddedOp);
int BuildEmbeddedOperandUses(GenTreeHWIntrinsic* embeddedOpNode, GenTree* embeddedDelayFreeOp);
int BuildContainedCselUses(GenTreeHWIntrinsic* containedCselOpNode,
GenTree* delayFreeOp,
Expand Down
106 changes: 83 additions & 23 deletions src/coreclr/jit/lsraarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1430,32 +1430,14 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou
if (embeddedOp != nullptr)
{
assert(delayFreeOp == nullptr);
delayFreeOp = getDelayFreeOperand(embeddedOp, /* embedded */ true);
delayFreeOp = getDelayFreeOperand(embeddedOp, intrinsicTree);
}

// Build any immediates
BuildHWIntrinsicImmediate(intrinsicTree, intrin);

// Build any additional special cases
switch (intrin.id)
{
case NI_Sve2_GatherVectorInt16SignExtendNonTemporal:
case NI_Sve2_GatherVectorInt32SignExtendNonTemporal:
case NI_Sve2_GatherVectorNonTemporal:
case NI_Sve2_GatherVectorUInt16ZeroExtendNonTemporal:
case NI_Sve2_GatherVectorUInt32ZeroExtendNonTemporal:
case NI_Sve2_Scatter16BitNarrowingNonTemporal:
case NI_Sve2_Scatter32BitNarrowingNonTemporal:
case NI_Sve2_ScatterNonTemporal:
if (!varTypeIsSIMD(intrin.op2->gtType))
{
buildInternalFloatRegisterDefForNode(intrinsicTree, internalFloatRegCandidates());
}
break;

default:
break;
}
// Build any internal temporary registers
BuildHWIntrinsicTempRegs(intrinsicTree, intrin, embeddedOp);

// Build all Operands
for (size_t opNum = 1; opNum <= intrin.numOperands; opNum++)
Expand Down Expand Up @@ -1809,6 +1791,47 @@ void LinearScan::BuildHWIntrinsicImmediate(GenTreeHWIntrinsic* intrinsicTree, co
}
}

//------------------------------------------------------------------------
// BuildHWIntrinsicTempRegs: Build temporary registers used by HWIntrinsic codegen.
//
// Arguments:
// intrinsicTree - Intrinsic tree node for which need to build RefPositions for
// intrin - Underlying intrinsic
// embeddedOp - Embedded mask operand of intrinsicTree, if any
//
void LinearScan::BuildHWIntrinsicTempRegs(GenTreeHWIntrinsic* intrinsicTree,
const HWIntrinsic intrin,
GenTreeHWIntrinsic* embeddedOp)
{
switch (intrin.id)
{
case NI_Sve2_GatherVectorInt16SignExtendNonTemporal:
case NI_Sve2_GatherVectorInt32SignExtendNonTemporal:
case NI_Sve2_GatherVectorNonTemporal:
case NI_Sve2_GatherVectorUInt16ZeroExtendNonTemporal:
case NI_Sve2_GatherVectorUInt32ZeroExtendNonTemporal:
case NI_Sve2_Scatter16BitNarrowingNonTemporal:
case NI_Sve2_Scatter32BitNarrowingNonTemporal:
case NI_Sve2_ScatterNonTemporal:
if (!varTypeIsSIMD(intrin.op2->gtType))
{
buildInternalFloatRegisterDefForNode(intrinsicTree, internalFloatRegCandidates());
}
break;

case NI_Sve_ConditionalSelect:
if ((embeddedOp != nullptr) && embeddedOp->OperIsHWIntrinsic(NI_Sve_MultiplyAddRotateComplex))
{
buildInternalFloatRegisterDefForNode(intrinsicTree, internalFloatRegCandidates());
setInternalRegsDelayFree = true;
}
break;

default:
break;
}
}

//------------------------------------------------------------------------
// BuildEmbeddedOperandUses: Build the uses for an Embedded Mask operand
//
Expand Down Expand Up @@ -2315,17 +2338,18 @@ SingleTypeRegSet LinearScan::getOperandCandidates(GenTreeHWIntrinsic* intrinsicT
//
// Arguments:
// intrinsicTree - Tree to check
// embedded - If this is an embedded operand
// user - The user of intrinsicTree if intrinsicTree is an embedded operand
//
// Return Value:
// The operand that needs to be delay freed
//
GenTree* LinearScan::getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, bool embedded)
GenTree* LinearScan::getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, GenTreeHWIntrinsic* user)
{
bool isRMW = intrinsicTree->isRMWHWIntrinsic(m_compiler);

const NamedIntrinsic intrinsicId = intrinsicTree->GetHWIntrinsicId();
GenTree* delayFreeOp = nullptr;
const bool embedded = user != nullptr;

switch (intrinsicId)
{
Expand Down Expand Up @@ -2381,6 +2405,23 @@ GenTree* LinearScan::getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, bool
assert(delayFreeOp != nullptr);
break;

case NI_Sve2_ConvertToSingleOdd:
case NI_Sve2_ConvertToSingleOddRoundToOdd:
assert(isRMW);
if (embedded && user->OperIsHWIntrinsic(NI_Sve_ConditionalSelect) && user->Op(3)->IsVectorZero() &&
!user->Op(1)->IsTrueMask(user->GetSimdBaseType()))
{
// These instructions cannot use movprfx. Prefer the zero falseOp as the target so codegen can
// preserve inactive lanes by first copying active lanes from op1.
delayFreeOp = user->Op(3);
}
else
{
delayFreeOp = intrinsicTree->Op(1);
}
assert(delayFreeOp != nullptr);
break;

default:
if (isRMW)
{
Expand All @@ -2406,6 +2447,25 @@ GenTree* LinearScan::getDelayFreeOperand(GenTreeHWIntrinsic* intrinsicTree, bool
assert(delayFreeOp != nullptr);
}
}
else if ((intrinsicTree->GetOperandCount() == 1) && embedded &&
!HWIntrinsicInfo::IsReduceOperation(intrinsicId))
{
// Unary embedded masked operations are non-RMW, but also support movprfx.
assert(user->OperIsHWIntrinsic(NI_Sve_ConditionalSelect));

if (user->Op(1)->IsTrueMask(user->GetSimdBaseType()) && user->Op(3)->IsVectorZero())
{
// When all lanes are active, falseOp is irrelevant and the embedded operation can write back to
// op1.
delayFreeOp = intrinsicTree->Op(1);
}
else
{
// Mark the falseOp of the conditional select as delay free.
delayFreeOp = user->Op(3);
}
assert(delayFreeOp != nullptr);
}
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2BaseType}* op2, {Op3VectorType}<{Op3BaseType}> op3, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, op3, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2BaseType}* op2, {Op3VectorType}<{Op3BaseType}> op3, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, op3, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2BaseType}* op2, {Op3VectorType}<{Op3BaseType}> op3, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, op3), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, op3, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op2BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Comment thread
dhartglassMSFT marked this conversation as resolved.
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({RetVectorType}<{RetBaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {RetVectorType}<{RetBaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {RetVectorType}<{RetBaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {RetVectorType}<{RetBaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue(Vector<{RetBaseType}> mask, Vector<{Op1BaseType}> op1, Vector<{Op2BaseType}> op2, Vector<{RetBaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == Vector<{RetBaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), Vector<{RetBaseType}>.Zero) :
Comment thread
dhartglassMSFT marked this conversation as resolved.
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
ValidateConditionalSelectResult(mask, op1, op2, falseOp, result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,9 @@ namespace {Namespace}
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op1BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp)
{
var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);
var result = (falseOp == {Op1VectorType}<{Op1BaseType}>.Zero) ?
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), {Op1VectorType}<{Op1BaseType}>.Zero) :
Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp);

Unsafe.Write(_dataTable.outArrayPtr, result);
ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr);
Expand Down
Loading
Loading