Accelerate Abs intrinsic even in cases where there is no direct instruction support...
authorsivarv <sivarv@microsoft.com>
Thu, 22 Dec 2016 22:59:32 +0000 (14:59 -0800)
committersivarv <sivarv@microsoft.com>
Wed, 4 Jan 2017 19:50:22 +0000 (11:50 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/6421a60dd64415f8689fbb14a16c8bbe19f7fa09

src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/gentree.cpp
src/coreclr/src/jit/importer.cpp
src/coreclr/src/jit/rationalize.cpp
src/coreclr/src/jit/simd.cpp
src/coreclr/src/jit/simdintrinsiclist.h
src/coreclr/tests/src/JIT/SIMD/VectorAbs.cs

index d8cd491..a16b61a 100644 (file)
@@ -1936,6 +1936,11 @@ public:
 
     GenTreePtr gtNewOneConNode(var_types type);
 
+#ifdef FEATURE_SIMD
+    GenTreePtr gtNewSIMDVectorZero(var_types simdType, var_types baseType, unsigned size);
+    GenTreePtr gtNewSIMDVectorOne(var_types simdType, var_types baseType, unsigned size);
+#endif
+
     GenTreeBlk* gtNewBlkOpNode(
         genTreeOps oper, GenTreePtr dst, GenTreePtr srcOrFillVal, GenTreePtr sizeOrClsTok, bool isVolatile);
 
@@ -7121,6 +7126,9 @@ private:
                                  GenTree**            op1,
                                  GenTree**            op2);
 
+    // Creates a GT_SIMD tree for Abs intrinsic.
+    GenTreePtr impSIMDAbs(CORINFO_CLASS_HANDLE typeHnd, var_types baseType, unsigned simdVectorSize, GenTree* op1);
+
 #if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
     // Transforms operands and returns the SIMD intrinsic to be applied on
     // transformed operands to obtain == comparison result.
index 4a6cc74..aa8acbe 100644 (file)
@@ -6787,6 +6787,57 @@ GenTreePtr Compiler::gtNewOneConNode(var_types type)
     }
 }
 
+#ifdef FEATURE_SIMD
+//---------------------------------------------------------------------
+// gtNewSIMDVectorZero: create a GT_SIMD node for Vector<T>.Zero
+//
+// Arguments:
+//    simdType  -  simd vector type
+//    baseType  -  element type of vector
+//    size      -  size of vector in bytes
+GenTreePtr Compiler::gtNewSIMDVectorZero(var_types simdType, var_types baseType, unsigned size)
+{
+    baseType         = genActualType(baseType);
+    GenTree* initVal = gtNewZeroConNode(baseType);
+    initVal->gtType  = baseType;
+    return gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size);
+}
+
+//---------------------------------------------------------------------
+// gtNewSIMDVectorOne: create a GT_SIMD node for Vector<T>.One
+//
+// Arguments:
+//    simdType  -  simd vector type
+//    baseType  -  element type of vector
+//    size      -  size of vector in bytes
+GenTreePtr Compiler::gtNewSIMDVectorOne(var_types simdType, var_types baseType, unsigned size)
+{
+    GenTree* initVal;
+    if (varTypeIsSmallInt(baseType))
+    {
+        unsigned baseSize = genTypeSize(baseType);
+        int      val;
+        if (baseSize == 1)
+        {
+            val = 0x01010101;
+        }
+        else
+        {
+            val = 0x00010001;
+        }
+        initVal = gtNewIconNode(val);
+    }
+    else
+    {
+        initVal = gtNewOneConNode(baseType);
+    }
+
+    baseType        = genActualType(baseType);
+    initVal->gtType = baseType;
+    return gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size);
+}
+#endif // FEATURE_SIMD
+
 GenTreeCall* Compiler::gtNewIndCallNode(GenTreePtr addr, var_types type, GenTreeArgList* args, IL_OFFSETX ilOffset)
 {
     return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, ilOffset);
index cb09ff8..9351094 100644 (file)
@@ -1638,21 +1638,52 @@ GenTreePtr Compiler::impNormStructVal(GenTreePtr           structVal,
 
         case GT_COMMA:
         {
-            // The second thing is the block node.
+            // The second thing could either be a block node or a GT_SIMD or a GT_COMMA node.
             GenTree* blockNode = structVal->gtOp.gtOp2;
             assert(blockNode->gtType == structType);
-            // It had better be a block node - any others should not occur here.
-            assert(blockNode->OperIsBlk());
-
-            // Sink the GT_COMMA below the blockNode addr.
-            GenTree* blockNodeAddr = blockNode->gtOp.gtOp1;
-            assert(blockNodeAddr->gtType == TYP_BYREF);
-            GenTree* commaNode    = structVal;
-            commaNode->gtType     = TYP_BYREF;
-            commaNode->gtOp.gtOp2 = blockNodeAddr;
-            blockNode->gtOp.gtOp1 = commaNode;
-            structVal             = blockNode;
-            alreadyNormalized     = true;
+
+            // Is this GT_COMMA(op1, GT_COMMA())?
+            GenTree* parent = structVal;
+            if (blockNode->OperGet() == GT_COMMA)
+            {
+                // Find the last node in the comma chain.
+                do
+                {
+                    assert(blockNode->gtType == structType);
+                    parent    = blockNode;
+                    blockNode = blockNode->gtOp.gtOp2;
+                } while (blockNode->OperGet() == GT_COMMA);
+            }
+
+#ifdef FEATURE_SIMD
+            if (blockNode->OperGet() == GT_SIMD)
+            {
+                parent->gtOp.gtOp2 = impNormStructVal(blockNode, structHnd, curLevel, forceNormalization);
+                alreadyNormalized  = true;
+            }
+            else
+#endif
+            {
+                assert(blockNode->OperIsBlk());
+
+                // Sink the GT_COMMA below the blockNode addr.
+                // That is GT_COMMA(op1, op2=blockNode) is tranformed into
+                // blockNode(GT_COMMA(TYP_BYREF, op1, op2's op1)).
+                //
+                // In case of a chained GT_COMMA case, we sink the last
+                // GT_COMMA below the blockNode addr.
+                GenTree* blockNodeAddr = blockNode->gtOp.gtOp1;
+                assert(blockNodeAddr->gtType == TYP_BYREF);
+                GenTree* commaNode    = parent;
+                commaNode->gtType     = TYP_BYREF;
+                commaNode->gtOp.gtOp2 = blockNodeAddr;
+                blockNode->gtOp.gtOp1 = commaNode;
+                if (parent == structVal)
+                {
+                    structVal = blockNode;
+                }
+                alreadyNormalized = true;
+            }
         }
         break;
 
index 7f5a26f..3cffaa0 100644 (file)
@@ -116,17 +116,14 @@ void Rationalizer::RewriteSIMDOperand(LIR::Use& use, bool keepBlk)
         addr->gtType = simdType;
         use.ReplaceWith(comp, addr);
     }
-#if defined(_TARGET_X86_)
-    // For x86, if we have GT_IND(GT_ADDR(GT_SIMD)), remove the GT_IND(GT_ADDR()), leaving just
-    // the GT_SIMD.
     else if ((addr->OperGet() == GT_ADDR) && (addr->gtGetOp1()->OperGet() == GT_SIMD))
     {
+        // if we have GT_IND(GT_ADDR(GT_SIMD)), remove the GT_IND(GT_ADDR()), leaving just the GT_SIMD.
         BlockRange().Remove(tree);
         BlockRange().Remove(addr);
 
         use.ReplaceWith(comp, addr->gtGetOp1());
     }
-#endif // defined(_TARGET_X86_)
     else if (!keepBlk)
     {
         tree->SetOper(GT_IND);
@@ -729,6 +726,11 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G
         case GT_IND:
             // Clear the `GTF_IND_ASG_LHS` flag, which overlaps with `GTF_IND_REQ_ADDR_IN_REG`.
             node->gtFlags &= ~GTF_IND_ASG_LHS;
+
+            if (varTypeIsSIMD(node))
+            {
+                RewriteSIMDOperand(use, false);
+            }
             break;
 
         case GT_NOP:
index 914ad94..e0f63c2 100644 (file)
@@ -1156,6 +1156,154 @@ SIMDIntrinsicID Compiler::impSIMDRelOp(SIMDIntrinsicID      relOpIntrinsicId,
 #endif // !_TARGET_XARCH_
 }
 
+//-------------------------------------------------------------------------
+// impSIMDAbs: creates GT_SIMD node to compute Abs value of a given vector.
+//
+// Arguments:
+//    typeHnd     -  type handle of SIMD vector
+//    baseType    -  base type of vector
+//    size        -  vector size in bytes
+//    op1         -  operand of Abs intrinsic
+//
+GenTreePtr Compiler::impSIMDAbs(CORINFO_CLASS_HANDLE typeHnd, var_types baseType, unsigned size, GenTree* op1)
+{
+    assert(varTypeIsSIMD(op1));
+
+    var_types  simdType = op1->TypeGet();
+    GenTreePtr retVal   = nullptr;
+
+#ifdef _TARGET_XARCH_
+    // When there is no direct support, Abs(v) could be computed
+    // on integer vectors as follows:
+    //     BitVector = v < vector.Zero
+    //     result = ConditionalSelect(BitVector, vector.Zero - v, v)
+
+    bool useConditionalSelect = false;
+    if (getSIMDInstructionSet() == InstructionSet_SSE2)
+    {
+        // SSE2 doesn't support abs on signed integer type vectors.
+        if (baseType == TYP_LONG || baseType == TYP_INT || baseType == TYP_SHORT || baseType == TYP_BYTE)
+        {
+            useConditionalSelect = true;
+        }
+    }
+    else
+    {
+        assert(getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+        if (baseType == TYP_LONG)
+        {
+            // SSE3_4/AVX2 don't support abs on long type vector.
+            useConditionalSelect = true;
+        }
+    }
+
+    if (useConditionalSelect)
+    {
+        // This works only on integer vectors not on float/double vectors.
+        assert(varTypeIsIntegral(baseType));
+
+        GenTreePtr op1Assign;
+        unsigned   op1LclNum;
+
+        if (op1->OperGet() == GT_LCL_VAR)
+        {
+            op1LclNum = op1->gtLclVarCommon.gtLclNum;
+            op1Assign = nullptr;
+        }
+        else
+        {
+            op1LclNum = lvaGrabTemp(true DEBUGARG("SIMD Abs op1"));
+            lvaSetStruct(op1LclNum, typeHnd, false);
+            op1Assign = gtNewTempAssign(op1LclNum, op1);
+            op1       = gtNewLclvNode(op1LclNum, op1->TypeGet());
+        }
+
+        // Assign Vector.Zero to a temp since it is needed more than once
+        GenTreePtr vecZero       = gtNewSIMDVectorZero(simdType, baseType, size);
+        unsigned   vecZeroLclNum = lvaGrabTemp(true DEBUGARG("SIMD Abs VecZero"));
+        lvaSetStruct(vecZeroLclNum, typeHnd, false);
+        GenTreePtr vecZeroAssign = gtNewTempAssign(vecZeroLclNum, vecZero);
+
+        // Construct BitVector = v < vector.Zero
+        GenTreePtr      bitVecOp1     = op1;
+        GenTreePtr      bitVecOp2     = gtNewLclvNode(vecZeroLclNum, vecZero->TypeGet());
+        var_types       relOpBaseType = baseType;
+        SIMDIntrinsicID relOpIntrinsic =
+            impSIMDRelOp(SIMDIntrinsicLessThan, typeHnd, size, &relOpBaseType, &bitVecOp1, &bitVecOp2);
+        GenTreePtr bitVec       = gtNewSIMDNode(simdType, bitVecOp1, bitVecOp2, relOpIntrinsic, relOpBaseType, size);
+        unsigned   bitVecLclNum = lvaGrabTemp(true DEBUGARG("SIMD Abs bitVec"));
+        lvaSetStruct(bitVecLclNum, typeHnd, false);
+        GenTreePtr bitVecAssign = gtNewTempAssign(bitVecLclNum, bitVec);
+        bitVec                  = gtNewLclvNode(bitVecLclNum, bitVec->TypeGet());
+
+        // Construct condSelectOp1 = vector.Zero - v
+        GenTreePtr subOp1 = gtNewLclvNode(vecZeroLclNum, vecZero->TypeGet());
+        GenTreePtr subOp2 = gtNewLclvNode(op1LclNum, op1->TypeGet());
+        GenTreePtr negVec = gtNewSIMDNode(simdType, subOp1, subOp2, SIMDIntrinsicSub, baseType, size);
+
+        // Construct ConditionalSelect(bitVec, vector.Zero - v, v)
+        GenTreePtr vec = gtNewLclvNode(op1LclNum, op1->TypeGet());
+        retVal         = impSIMDSelect(typeHnd, baseType, size, bitVec, negVec, vec);
+
+        // Prepend bitVec assignment to retVal.
+        // retVal = (tmp2 = v < tmp1), CondSelect(tmp2, tmp1 - v, v)
+        retVal = gtNewOperNode(GT_COMMA, simdType, bitVecAssign, retVal);
+
+        // Prepend vecZero assignment to retVal.
+        // retVal =  (tmp1 = vector.Zero), (tmp2 = v < tmp1), CondSelect(tmp2, tmp1 - v, v)
+        retVal = gtNewOperNode(GT_COMMA, simdType, vecZeroAssign, retVal);
+
+        // If op1 was assigned to a temp, prepend that to retVal.
+        if (op1Assign != nullptr)
+        {
+            // retVal = (v=op1), (tmp1 = vector.Zero), (tmp2 = v < tmp1), CondSelect(tmp2, tmp1 - v, v)
+            retVal = gtNewOperNode(GT_COMMA, simdType, op1Assign, retVal);
+        }
+    }
+    else if (varTypeIsFloating(baseType))
+    {
+        // Abs(vf) = vf & new SIMDVector<float>(0x7fffffff);
+        // Abs(vd) = vf & new SIMDVector<double>(0x7fffffffffffffff);
+        GenTree* bitMask = nullptr;
+        if (baseType == TYP_FLOAT)
+        {
+            float f;
+            static_assert_no_msg(sizeof(float) == sizeof(int));
+            *((int*)&f) = 0x7fffffff;
+            bitMask     = gtNewDconNode(f);
+        }
+        else if (baseType == TYP_DOUBLE)
+        {
+            double d;
+            static_assert_no_msg(sizeof(double) == sizeof(__int64));
+            *((__int64*)&d) = 0x7fffffffffffffffLL;
+            bitMask         = gtNewDconNode(d);
+        }
+
+        assert(bitMask != nullptr);
+        bitMask->gtType        = baseType;
+        GenTree* bitMaskVector = gtNewSIMDNode(simdType, bitMask, SIMDIntrinsicInit, baseType, size);
+        retVal                 = gtNewSIMDNode(simdType, op1, bitMaskVector, SIMDIntrinsicBitwiseAnd, baseType, size);
+    }
+    else if (baseType == TYP_CHAR || baseType == TYP_UBYTE || baseType == TYP_UINT || baseType == TYP_ULONG)
+    {
+        // Abs is a no-op on unsigned integer type vectors
+        retVal = op1;
+    }
+    else
+    {
+        assert(getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+        assert(baseType != TYP_LONG);
+
+        retVal = gtNewSIMDNode(simdType, op1, SIMDIntrinsicAbs, baseType, size);
+    }
+#else  // !_TARGET_XARCH_
+    assert(!"Abs intrinsic on non-xarch target not implemented");
+#endif // !_TARGET_XARCH_
+
+    return retVal;
+}
+
 // Creates a GT_SIMD tree for Select operation
 //
 // Argumens:
@@ -1820,43 +1968,12 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE                opcode,
         break;
 
         case SIMDIntrinsicGetZero:
-        {
-            baseType         = genActualType(baseType);
-            GenTree* initVal = gtNewZeroConNode(baseType);
-            initVal->gtType  = baseType;
-            simdTree         = gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size);
-            retVal           = simdTree;
-        }
-        break;
+            retVal = gtNewSIMDVectorZero(simdType, baseType, size);
+            break;
 
         case SIMDIntrinsicGetOne:
-        {
-            GenTree* initVal;
-            if (varTypeIsSmallInt(baseType))
-            {
-                unsigned baseSize = genTypeSize(baseType);
-                int      val;
-                if (baseSize == 1)
-                {
-                    val = 0x01010101;
-                }
-                else
-                {
-                    val = 0x00010001;
-                }
-                initVal = gtNewIconNode(val);
-            }
-            else
-            {
-                initVal = gtNewOneConNode(baseType);
-            }
-
-            baseType        = genActualType(baseType);
-            initVal->gtType = baseType;
-            simdTree        = gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size);
-            retVal          = simdTree;
-        }
-        break;
+            retVal = gtNewSIMDVectorOne(simdType, baseType, size);
+            break;
 
         case SIMDIntrinsicGetAllOnes:
         {
@@ -2443,76 +2560,9 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE                opcode,
         break;
 
         case SIMDIntrinsicAbs:
-        {
-#ifdef _TARGET_XARCH_
-            // TODO-CQ-XARCH: we should be able to use conditional select
-            // to compute abs(v) as follows when there is no direct support:
-            //     BitVector = v < vector.Zero
-            //     result = ConditionalSelect(BitVector, vector.Zero - v, v)
-            if (getSIMDInstructionSet() == InstructionSet_SSE2)
-            {
-                // SSE2 doesn't support abs on signed integer type vectors.
-                if (baseType == TYP_LONG || baseType == TYP_INT || baseType == TYP_SHORT || baseType == TYP_BYTE)
-                {
-                    return nullptr;
-                }
-            }
-            else
-            {
-                assert(getSIMDInstructionSet() >= InstructionSet_SSE3_4);
-                if (baseType == TYP_LONG)
-                {
-                    // SSE3_4/AVX2 don't support abs on long type vector.
-                    return nullptr;
-                }
-            }
-
-            op1 = impSIMDPopStack(simdType);
-
-            if (varTypeIsFloating(baseType))
-            {
-                // Abs(vf) = vf & new SIMDVector<float>(0x7fffffff);
-                // Abs(vd) = vf & new SIMDVector<double>(0x7fffffffffffffff);
-                GenTree* bitMask = nullptr;
-                if (baseType == TYP_FLOAT)
-                {
-                    float f;
-                    static_assert_no_msg(sizeof(float) == sizeof(int));
-                    *((int*)&f) = 0x7fffffff;
-                    bitMask     = gtNewDconNode(f);
-                }
-                else if (baseType == TYP_DOUBLE)
-                {
-                    double d;
-                    static_assert_no_msg(sizeof(double) == sizeof(__int64));
-                    *((__int64*)&d) = 0x7fffffffffffffffLL;
-                    bitMask         = gtNewDconNode(d);
-                }
-
-                assert(bitMask != nullptr);
-                bitMask->gtType        = baseType;
-                GenTree* bitMaskVector = gtNewSIMDNode(simdType, bitMask, SIMDIntrinsicInit, baseType, size);
-                retVal = gtNewSIMDNode(simdType, op1, bitMaskVector, SIMDIntrinsicBitwiseAnd, baseType, size);
-            }
-            else if (baseType == TYP_CHAR || baseType == TYP_UBYTE || baseType == TYP_UINT || baseType == TYP_ULONG)
-            {
-                // Abs is a no-op on unsigned integer type vectors
-                retVal = op1;
-            }
-            else
-            {
-                assert(getSIMDInstructionSet() >= InstructionSet_SSE3_4);
-                assert(baseType != TYP_LONG);
-
-                retVal = gtNewSIMDNode(simdType, op1, SIMDIntrinsicAbs, baseType, size);
-            }
-
-#else // !_TARGET_XARCH_
-            assert(!"Abs intrinsic on non-xarch target not implemented");
-            unreached();
-#endif // !_TARGET_XARCH_
-        }
-        break;
+            op1    = impSIMDPopStack(simdType);
+            retVal = impSIMDAbs(clsHnd, baseType, size, op1);
+            break;
 
         case SIMDIntrinsicGetW:
             retVal = impSIMDGetFixed(simdType, baseType, size, 3);
index c717c14..0160582 100644 (file)
@@ -89,13 +89,12 @@ SIMD_INTRINSIC("op_Subtraction",            false,       Sub,
 SIMD_INTRINSIC("op_Multiply",               false,       Mul,                      "*",                      TYP_STRUCT,     2,      {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF},   {TYP_INT, TYP_FLOAT, TYP_DOUBLE, TYP_SHORT,TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
 SIMD_INTRINSIC("op_Division",               false,       Div,                      "/",                      TYP_STRUCT,     2,      {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF},   {TYP_FLOAT, TYP_DOUBLE, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
 
-// Abs and SquareRoot are recognized as intrinsics only in case of float or double vectors
-SIMD_INTRINSIC("Abs",                       false,       Abs,                      "abs",                    TYP_STRUCT,     1,      {TYP_STRUCT, TYP_UNDEF, TYP_UNDEF},    {TYP_FLOAT, TYP_DOUBLE, TYP_CHAR, TYP_UBYTE, TYP_UINT, TYP_ULONG, TYP_INT, TYP_SHORT, TYP_BYTE, TYP_UNDEF})
+// SquareRoot is recognized as an intrinsic only for float or double vectors
 SIMD_INTRINSIC("SquareRoot",                false,       Sqrt,                     "sqrt",                   TYP_STRUCT,     1,      {TYP_STRUCT, TYP_UNDEF, TYP_UNDEF},    {TYP_FLOAT, TYP_DOUBLE, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF, TYP_UNDEF})
 
-// Min and max methods are recognized as intrinsics only in case of float or double vectors
 SIMD_INTRINSIC("Min",                       false,       Min,                      "min",                    TYP_STRUCT,     2,      {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF},   {TYP_INT, TYP_FLOAT, TYP_DOUBLE, TYP_LONG, TYP_CHAR, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_ULONG})
 SIMD_INTRINSIC("Max",                       false,       Max,                      "max",                    TYP_STRUCT,     2,      {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF},   {TYP_INT, TYP_FLOAT, TYP_DOUBLE, TYP_LONG, TYP_CHAR, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_ULONG})
+SIMD_INTRINSIC("Abs",                       false,       Abs,                      "abs",                    TYP_STRUCT,     1,      {TYP_STRUCT, TYP_UNDEF, TYP_UNDEF },   {TYP_INT, TYP_FLOAT, TYP_DOUBLE, TYP_LONG, TYP_CHAR, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_ULONG})
 
 // Vector Relational operators
 SIMD_INTRINSIC("Equals",                    false,       Equal,                    "eq",                     TYP_STRUCT,     2,      {TYP_STRUCT, TYP_STRUCT, TYP_UNDEF},   {TYP_INT, TYP_FLOAT, TYP_DOUBLE, TYP_LONG, TYP_CHAR, TYP_UBYTE, TYP_BYTE, TYP_SHORT, TYP_UINT, TYP_ULONG})
index ab18272..6a8233c 100644 (file)
@@ -89,10 +89,10 @@ internal partial class VectorTest
         JitLog jitLog = new JitLog();
         if (!jitLog.Check("Abs", "Single")) returnVal = Fail;
         if (!jitLog.Check("Abs", "Double")) returnVal = Fail;
-        // SSE2: Abs is not an intrinsic for Int32 and Int64, but IS for UInt32 and UInt64
-        // SSE3_4: Abs is not an intrinsic for Int64 alone.
-        // Since right now there is no way to know SIMD instruction set used by JIT, 
-        // we will check conservatively on SSE3_4 targets.
+        if (!jitLog.Check("Abs", "Int64")) returnVal = Fail;
+        if (!jitLog.Check("Abs", "Int32")) returnVal = Fail;
+        if (!jitLog.Check("Abs", "Int16")) returnVal = Fail;
+        if (!jitLog.Check("Abs", "SByte")) returnVal = Fail;
         if (!jitLog.Check("System.Numerics.Vector4:Abs")) returnVal = Fail;
         if (!jitLog.Check("System.Numerics.Vector3:Abs")) returnVal = Fail;
         if (!jitLog.Check("System.Numerics.Vector2:Abs")) returnVal = Fail;
@@ -101,13 +101,6 @@ internal partial class VectorTest
         if (!jitLog.Check("Abs", "UInt32")) returnVal = Fail;
         if (!jitLog.Check("Abs", "UInt64")) returnVal = Fail;
 
-        // AVX: Abs is not an intrinsic for Int64 alone.
-        if (Vector<int>.Count == 8)
-        {
-            if (!jitLog.Check("Abs", "Int32")) returnVal = Fail;
-            if (!jitLog.Check("Abs", "Int16")) returnVal = Fail;
-            if (!jitLog.Check("Abs", "SByte")) returnVal = Fail;
-        }
         jitLog.Dispose();
 
         return returnVal;