NYI("genHWIntrinsicSimdExtractOp not implemented");
}
+//------------------------------------------------------------------------
+// genHWIntrinsicSimdSelectOp:
+//
+// Produce code for a GT_HWIntrinsic node with form SimdSelectOp.
+//
+// Consumes three SIMD operands and produces a SIMD result
+//
+// This intrinsic form requires one of the source registers to be the
+// destination register. Inserts a INS_mov if this requirement is not met.
+//
+// Arguments:
+// node - the GT_HWIntrinsic node
+//
+// Return Value:
+// None.
+//
void CodeGen::genHWIntrinsicSimdSelectOp(GenTreeHWIntrinsic* node)
{
- NYI("genHWIntrinsicSimdSelectOp not implemented");
+ GenTreeArgList* argList = node->gtGetOp1()->AsArgList();
+ GenTree* op1 = argList->Current();
+ GenTree* op2 = argList->Rest()->Current();
+ GenTree* op3 = argList->Rest()->Rest()->Current();
+ var_types baseType = node->gtSIMDBaseType;
+ regNumber targetReg = node->gtRegNum;
+
+ assert(targetReg != REG_NA);
+ var_types targetType = node->TypeGet();
+
+ genConsumeRegs(op1);
+ genConsumeRegs(op2);
+ genConsumeRegs(op3);
+
+ regNumber op1Reg = op1->gtRegNum;
+ regNumber op2Reg = op2->gtRegNum;
+ regNumber op3Reg = op3->gtRegNum;
+
+ assert(genIsValidFloatReg(op1Reg));
+ assert(genIsValidFloatReg(op2Reg));
+ assert(genIsValidFloatReg(op3Reg));
+ assert(genIsValidFloatReg(targetReg));
+
+ bool is16Byte = (node->gtSIMDSize > 8);
+ emitAttr attr = is16Byte ? EA_16BYTE : EA_8BYTE;
+
+ // Arm64 has three bit select forms; each uses three source registers
+ // One of the sources is also the destination
+ if (targetReg == op3Reg)
+ {
+ // op3 is target use bit insert if true
+ // op3 = op3 ^ (op1 & (op2 ^ op3))
+ getEmitter()->emitIns_R_R_R(INS_bit, attr, op3Reg, op2Reg, op1Reg);
+ }
+ else if (targetReg == op2Reg)
+ {
+ // op2 is target use bit insert if false
+ // op2 = op2 ^ (~op1 & (op2 ^ op3))
+ getEmitter()->emitIns_R_R_R(INS_bif, attr, op2Reg, op3Reg, op1Reg);
+ }
+ else
+ {
+ if (targetReg != op1Reg)
+ {
+ // target is not one of the sources, copy op1 to use bit select form
+ getEmitter()->emitIns_R_R(INS_mov, attr, targetReg, op1Reg);
+ }
+ // use bit select
+ // targetReg = op3 ^ (targetReg & (op2 ^ op3))
+ getEmitter()->emitIns_R_R_R(INS_bsl, attr, targetReg, op2Reg, op3Reg);
+ }
+
+ genProduceReg(node);
}
void CodeGen::genHWIntrinsicSimdUnaryOp(GenTreeHWIntrinsic* node)
switch (getHWIntrinsicInfo(intrinsic).form)
{
case HWIntrinsicInfo::SimdBinaryOp:
+ case HWIntrinsicInfo::SimdSelectOp:
case HWIntrinsicInfo::SimdUnaryOp:
simdClass = sig->retTypeClass;
break;
return gtNewSimdHWIntrinsicNode(simdType, op1, op2, intrinsic, simdBaseType, simdSizeBytes);
+ case HWIntrinsicInfo::SimdSelectOp:
+ // op1 is the first operand
+ // op2 is the second operand
+ // op3 is the third operand
+ op3 = impSIMDPopStack(simdType);
+ op2 = impSIMDPopStack(simdType);
+ op1 = impSIMDPopStack(simdType);
+
+ return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes);
+
case HWIntrinsicInfo::SimdUnaryOp:
op1 = impSIMDPopStack(simdType);
void LinearScan::TreeNodeInfoInitHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, TreeNodeInfo* info)
{
NamedIntrinsic intrinsicID = intrinsicTree->gtHWIntrinsicId;
- info->srcCount += GetOperandInfo(intrinsicTree->gtOp.gtOp1);
- if (intrinsicTree->gtGetOp2IfPresent() != nullptr)
+
+ GenTreeArgList* argList = nullptr;
+ GenTree* op1 = intrinsicTree->gtOp.gtOp1;
+ GenTree* op2 = intrinsicTree->gtOp.gtOp2;
+
+ if (op1->OperIs(GT_LIST))
+ {
+ argList = op1->AsArgList();
+ op1 = argList->Current();
+ op2 = argList->Rest()->Current();
+
+ for (GenTreeArgList* list = argList; list != nullptr; list = list->Rest())
+ {
+ info->srcCount += GetOperandInfo(list->Current());
+ }
+ }
+ else
{
- info->srcCount += GetOperandInfo(intrinsicTree->gtOp.gtOp2);
+ info->srcCount += GetOperandInfo(op1);
+ if (op2 != nullptr)
+ {
+ info->srcCount += GetOperandInfo(op2);
+ }
}
switch (compiler->getHWIntrinsicInfo(intrinsicID).form)
{
case HWIntrinsicInfo::SimdExtractOp:
- if (!intrinsicTree->gtOp.gtOp2->isContained())
+ if (!op2->isContained())
{
// We need a temp to create a switch table
info->internalIntCount = 1;
info->setInternalCandidates(this, allRegs(TYP_INT));
}
break;
+
default:
break;
}