diff options
author | Bruce Forstall <brucefo@microsoft.com> | 2016-12-14 18:03:20 -0800 |
---|---|---|
committer | Bruce Forstall <brucefo@microsoft.com> | 2017-02-05 21:23:02 -0800 |
commit | cacb79692c4db6c4dded4d8f6a55e7fd8fa11d3a (patch) | |
tree | 8c3ef8163e7a994a55cd65d1a57d9dd9dc783de0 /src/jit/decomposelongs.cpp | |
parent | df659fc58c602eb079d3620e9e9925d3c78318f4 (diff) | |
download | coreclr-cacb79692c4db6c4dded4d8f6a55e7fd8fa11d3a.tar.gz coreclr-cacb79692c4db6c4dded4d8f6a55e7fd8fa11d3a.tar.bz2 coreclr-cacb79692c4db6c4dded4d8f6a55e7fd8fa11d3a.zip |
Enable SIMD for RyuJIT/x86
This change implements support for Vector<long>, handling
SIMDIntrinsicInit, which takes a LONG, and decomposition of
SIMDIntrinsicGetItem, which produces a LONG.
It also enables SIMD, including AVX, by default for RyuJIT/x86.
Diffstat (limited to 'src/jit/decomposelongs.cpp')
-rw-r--r-- | src/jit/decomposelongs.cpp | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp index 2716631abc..4dad1c0294 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: @@ -1562,6 +1568,129 @@ 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); + + 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()); + + LIR::Use op2(Range(), &simdTree->gtOp.gtOp2, simdTree); + unsigned indexTmpVarNum = op2.ReplaceWithLclVar(m_compiler, m_blockWeight); + JITDUMP("[DecomposeSimdGetItem]: Saving op2 tree to a temp var:\n"); + DISPTREERANGE(Range(), op2.Def()); + + // TODO-CQ: if the index is constant, we don't need to do the computation dynamically. + + // Create: + // loResult = GT_SIMD{get_item}[int](tmp_simd_var, tmp_index * 2) + + GenTree* simdTmpVar1 = m_compiler->gtNewLclLNode(simdTmpVarNum, simdTree->gtOp.gtOp1->gtType); + GenTree* indexTmpVar1 = m_compiler->gtNewLclLNode(indexTmpVarNum, TYP_INT); + GenTree* two1 = m_compiler->gtNewIconNode(2, TYP_INT); + GenTree* indexTimesTwo1 = m_compiler->gtNewOperNode(GT_MUL, TYP_INT, indexTmpVar1, two1); + + GenTree* loResult = + m_compiler->gtNewSIMDNode(TYP_INT, simdTmpVar1, indexTimesTwo1, SIMDIntrinsicGetItem, TYP_INT, simdSize); + + // Create: + // hiResult = GT_SIMD{get_item}[int](tmp_simd_var, tmp_index * 2 + 1) + + GenTree* simdTmpVar2 = m_compiler->gtNewLclLNode(simdTmpVarNum, simdTree->gtOp.gtOp1->gtType); + 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); + GenTree* indexTimesTwoPlusOne = m_compiler->gtNewOperNode(GT_ADD, TYP_INT, indexTimesTwo2, one); + + GenTree* hiResult = + m_compiler->gtNewSIMDNode(TYP_INT, simdTmpVar2, indexTimesTwoPlusOne, SIMDIntrinsicGetItem, TYP_INT, simdSize); + + // Put all the new nodes in execution order. + + Range().InsertBefore(tree, simdTmpVar1, indexTmpVar1, two1, indexTimesTwo1); + Range().InsertBefore(tree, loResult, simdTmpVar2, indexTmpVar2, two2); + Range().InsertBefore(tree, indexTimesTwo2, one, indexTimesTwoPlusOne, hiResult); + + Range().Remove(tree); + + 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. |