summaryrefslogtreecommitdiff
path: root/src/jit/decomposelongs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/decomposelongs.cpp')
-rw-r--r--src/jit/decomposelongs.cpp165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp
index 98b8b08..407ae1c 100644
--- a/src/jit/decomposelongs.cpp
+++ b/src/jit/decomposelongs.cpp
@@ -249,6 +249,12 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree)
nextNode = DecomposeRotate(use);
break;
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ nextNode = DecomposeSimd(use);
+ break;
+#endif // FEATURE_SIMD
+
case GT_LOCKADD:
case GT_XADD:
case GT_XCHG:
@@ -411,6 +417,8 @@ GenTree* DecomposeLongs::DecomposeLclFld(LIR::Use& use)
GenTree* hiResult = m_compiler->gtNewLclFldNode(loResult->gtLclNum, TYP_INT, loResult->gtLclOffs + 4);
Range().InsertAfter(loResult, hiResult);
+ m_compiler->lvaIncRefCnts(hiResult);
+
return FinalizeDecomposition(use, loResult, hiResult, hiResult);
}
@@ -1560,6 +1568,163 @@ GenTree* DecomposeLongs::DecomposeUMod(LIR::Use& use)
return FinalizeDecomposition(use, loResult, hiResult, hiResult);
}
+#ifdef FEATURE_SIMD
+
+//------------------------------------------------------------------------
+// DecomposeSimd: Decompose GT_SIMD.
+//
+// Arguments:
+// use - the LIR::Use object for the def that needs to be decomposed.
+//
+// Return Value:
+// The next node to process.
+//
+GenTree* DecomposeLongs::DecomposeSimd(LIR::Use& use)
+{
+ GenTree* tree = use.Def();
+ genTreeOps oper = tree->OperGet();
+
+ assert(oper == GT_SIMD);
+
+ GenTreeSIMD* simdTree = tree->AsSIMD();
+
+ switch (simdTree->gtSIMDIntrinsicID)
+ {
+ case SIMDIntrinsicGetItem:
+ return DecomposeSimdGetItem(use);
+
+ default:
+ noway_assert(!"unexpected GT_SIMD node in long decomposition");
+ break;
+ }
+
+ return nullptr;
+}
+
+//------------------------------------------------------------------------
+// DecomposeSimdGetItem: Decompose GT_SIMD -- SIMDIntrinsicGetItem.
+//
+// Decompose a get[i] node on Vector<long>. For:
+//
+// GT_SIMD{get_item}[long](simd_var, index)
+//
+// create:
+//
+// tmp_simd_var = simd_var
+// tmp_index = index
+// loResult = GT_SIMD{get_item}[int](tmp_simd_var, tmp_index * 2)
+// hiResult = GT_SIMD{get_item}[int](tmp_simd_var, tmp_index * 2 + 1)
+// return: GT_LONG(loResult, hiResult)
+//
+// This isn't optimal codegen, since SIMDIntrinsicGetItem sometimes requires
+// temps that could be shared, for example.
+//
+// Arguments:
+// use - the LIR::Use object for the def that needs to be decomposed.
+//
+// Return Value:
+// The next node to process.
+//
+GenTree* DecomposeLongs::DecomposeSimdGetItem(LIR::Use& use)
+{
+ GenTree* tree = use.Def();
+ genTreeOps oper = tree->OperGet();
+
+ assert(oper == GT_SIMD);
+
+ GenTreeSIMD* simdTree = tree->AsSIMD();
+ var_types baseType = simdTree->gtSIMDBaseType;
+ unsigned simdSize = simdTree->gtSIMDSize;
+
+ assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicGetItem);
+ assert(varTypeIsLong(baseType));
+ assert(varTypeIsLong(simdTree));
+ assert(varTypeIsSIMD(simdTree->gtOp.gtOp1->gtType));
+ assert(simdTree->gtOp.gtOp2->gtType == TYP_INT);
+
+ bool indexIsConst = simdTree->gtOp.gtOp2->IsCnsIntOrI();
+ ssize_t index = 0;
+ if (indexIsConst)
+ {
+ index = simdTree->gtOp.gtOp2->gtIntCon.gtIconVal;
+ }
+
+ LIR::Use op1(Range(), &simdTree->gtOp.gtOp1, simdTree);
+ unsigned simdTmpVarNum = op1.ReplaceWithLclVar(m_compiler, m_blockWeight);
+ JITDUMP("[DecomposeSimdGetItem]: Saving op1 tree to a temp var:\n");
+ DISPTREERANGE(Range(), op1.Def());
+
+ unsigned indexTmpVarNum = 0;
+ if (!indexIsConst)
+ {
+ LIR::Use op2(Range(), &simdTree->gtOp.gtOp2, simdTree);
+ indexTmpVarNum = op2.ReplaceWithLclVar(m_compiler, m_blockWeight);
+ JITDUMP("[DecomposeSimdGetItem]: Saving op2 tree to a temp var:\n");
+ DISPTREERANGE(Range(), op2.Def());
+ }
+
+ // Create:
+ // loResult = GT_SIMD{get_item}[int](tmp_simd_var, index * 2)
+
+ GenTree* simdTmpVar1 = m_compiler->gtNewLclLNode(simdTmpVarNum, simdTree->gtOp.gtOp1->gtType);
+ GenTree* indexTimesTwo1;
+
+ if (indexIsConst)
+ {
+ // Reuse the existing index constant node.
+ indexTimesTwo1 = simdTree->gtOp.gtOp2;
+ Range().Remove(indexTimesTwo1);
+ indexTimesTwo1->gtIntCon.gtIconVal = index * 2;
+
+ Range().InsertBefore(simdTree, simdTmpVar1, indexTimesTwo1);
+ }
+ else
+ {
+ GenTree* indexTmpVar1 = m_compiler->gtNewLclLNode(indexTmpVarNum, TYP_INT);
+ GenTree* two1 = m_compiler->gtNewIconNode(2, TYP_INT);
+ indexTimesTwo1 = m_compiler->gtNewOperNode(GT_MUL, TYP_INT, indexTmpVar1, two1);
+ Range().InsertBefore(simdTree, simdTmpVar1, indexTmpVar1, two1, indexTimesTwo1);
+ }
+
+ GenTree* loResult =
+ m_compiler->gtNewSIMDNode(TYP_INT, simdTmpVar1, indexTimesTwo1, SIMDIntrinsicGetItem, TYP_INT, simdSize);
+ Range().InsertBefore(simdTree, loResult);
+
+ // Create:
+ // hiResult = GT_SIMD{get_item}[int](tmp_simd_var, index * 2 + 1)
+
+ GenTree* simdTmpVar2 = m_compiler->gtNewLclLNode(simdTmpVarNum, simdTree->gtOp.gtOp1->gtType);
+ GenTree* indexTimesTwoPlusOne;
+
+ if (indexIsConst)
+ {
+ indexTimesTwoPlusOne = m_compiler->gtNewIconNode(index * 2 + 1, TYP_INT);
+ Range().InsertBefore(simdTree, simdTmpVar2, indexTimesTwoPlusOne);
+ }
+ else
+ {
+ GenTree* indexTmpVar2 = m_compiler->gtNewLclLNode(indexTmpVarNum, TYP_INT);
+ GenTree* two2 = m_compiler->gtNewIconNode(2, TYP_INT);
+ GenTree* indexTimesTwo2 = m_compiler->gtNewOperNode(GT_MUL, TYP_INT, indexTmpVar2, two2);
+ GenTree* one = m_compiler->gtNewIconNode(1, TYP_INT);
+ indexTimesTwoPlusOne = m_compiler->gtNewOperNode(GT_ADD, TYP_INT, indexTimesTwo2, one);
+ Range().InsertBefore(simdTree, simdTmpVar2, indexTmpVar2, two2, indexTimesTwo2);
+ Range().InsertBefore(simdTree, one, indexTimesTwoPlusOne);
+ }
+
+ GenTree* hiResult =
+ m_compiler->gtNewSIMDNode(TYP_INT, simdTmpVar2, indexTimesTwoPlusOne, SIMDIntrinsicGetItem, TYP_INT, simdSize);
+ Range().InsertBefore(simdTree, hiResult);
+
+ // Done with the original tree; remove it.
+
+ Range().Remove(simdTree);
+
+ return FinalizeDecomposition(use, loResult, hiResult, hiResult);
+}
+
+#endif // FEATURE_SIMD
+
//------------------------------------------------------------------------
// StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't,
// store the node to a var. Then decompose the new LclVar.