summaryrefslogtreecommitdiff
path: root/src/jit/simd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/simd.cpp')
-rw-r--r--src/jit/simd.cpp257
1 files changed, 160 insertions, 97 deletions
diff --git a/src/jit/simd.cpp b/src/jit/simd.cpp
index 39664c47bf..fb190c4fa1 100644
--- a/src/jit/simd.cpp
+++ b/src/jit/simd.cpp
@@ -347,6 +347,7 @@ var_types Compiler::getBaseTypeAndSizeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd, u
}
*sizeBytes = size;
+ setUsesSIMDTypes(true);
}
return simdBaseType;
@@ -426,16 +427,6 @@ const SIMDIntrinsicInfo* Compiler::getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* in
return nullptr;
}
-#ifdef _TARGET_X86_
- // NYI: support LONG type SIMD intrinsics. Need support in long decomposition.
- // (Don't use NYI fallback mechanism; just call the function.)
- if ((*baseType == TYP_LONG) || (*baseType == TYP_ULONG))
- {
- JITDUMP("NYI: x86 long base type SIMD intrinsics\n");
- return nullptr;
- }
-#endif // _TARGET_X86_
-
// account for implicit "this" arg
*argCount = sig->numArgs;
if (sig->hasThis())
@@ -1156,6 +1147,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:
@@ -1645,7 +1784,7 @@ GenTreePtr Compiler::createAddressNodeForSIMDInit(GenTreePtr tree, unsigned simd
GenTreeArrLen* arrLen =
new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrayRef, (int)offsetof(CORINFO_Array, length));
GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr, SCK_RNGCHK_FAIL);
+ GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, SCK_RNGCHK_FAIL);
offset += offsetof(CORINFO_Array, u1Elems);
byrefNode = gtNewOperNode(GT_COMMA, arrayRef->TypeGet(), arrBndsChk, gtCloneExpr(arrayRef));
@@ -1820,43 +1959,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:
{
@@ -2130,7 +2238,7 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH)
GenTreeArrLen(TYP_INT, arrayRefForArgRngChk, (int)offsetof(CORINFO_Array, length));
argRngChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, index, op3CheckKind);
+ GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, arrLen, op3CheckKind);
// Now, clone op3 to create another node for the argChk
GenTree* index2 = gtCloneExpr(op3);
assert(index != nullptr);
@@ -2151,7 +2259,7 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH)
GenTreeArrLen(TYP_INT, arrayRefForArgChk, (int)offsetof(CORINFO_Array, length));
GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK)
- GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, arrLen, checkIndexExpr, op2CheckKind);
+ GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, op2CheckKind);
// Create a GT_COMMA tree for the bounds check(s).
op2 = gtNewOperNode(GT_COMMA, op2->TypeGet(), argChk, op2);
@@ -2383,7 +2491,7 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
GenTree* lengthNode = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, vectorLength);
GenTreeBoundsChk* simdChk =
- new (this, GT_SIMD_CHK) GenTreeBoundsChk(GT_SIMD_CHK, TYP_VOID, lengthNode, index, SCK_RNGCHK_FAIL);
+ new (this, GT_SIMD_CHK) GenTreeBoundsChk(GT_SIMD_CHK, TYP_VOID, index, lengthNode, SCK_RNGCHK_FAIL);
// Create a GT_COMMA tree for the bounds check.
op2 = gtNewOperNode(GT_COMMA, op2->TypeGet(), simdChk, op2);
@@ -2443,54 +2551,9 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
break;
case SIMDIntrinsicAbs:
- {
- op1 = impSIMDPopStack(simdType);
-
-#ifdef _TARGET_XARCH_
- 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
- {
- // SSE/AVX doesn't support abs on signed integer vectors and hence
- // should never be seen as an intrinsic here. See SIMDIntrinsicList.h
- // for supported base types for this intrinsic.
- unreached();
- }
-
-#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);