[Arm64] Implement Simd.BitwiseSelect
authorSteve MacLean <sdmaclea.qdt@qualcommdatacenter.com>
Tue, 30 Jan 2018 16:21:09 +0000 (11:21 -0500)
committerTanner Gooding <tagoo@outlook.com>
Thu, 1 Feb 2018 15:14:04 +0000 (07:14 -0800)
src/jit/codegenarm64.cpp
src/jit/hwintrinsicArm64.cpp
src/jit/lsraarm64.cpp

index 112e77d..ce61de7 100644 (file)
@@ -5251,9 +5251,77 @@ void CodeGen::genHWIntrinsicSimdInsertOp(GenTreeHWIntrinsic* node)
     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)
index 08a59cb..1acf242 100644 (file)
@@ -162,6 +162,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic        intrinsic,
     switch (getHWIntrinsicInfo(intrinsic).form)
     {
         case HWIntrinsicInfo::SimdBinaryOp:
+        case HWIntrinsicInfo::SimdSelectOp:
         case HWIntrinsicInfo::SimdUnaryOp:
             simdClass = sig->retTypeClass;
             break;
@@ -200,6 +201,16 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic        intrinsic,
 
             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);
 
index ec1e7f0..8dd299f 100644 (file)
@@ -995,22 +995,42 @@ void LinearScan::TreeNodeInfoInitSIMD(GenTreeSIMD* simdTree, TreeNodeInfo* info)
 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;
     }