diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index d877d2199d886a..fc97512fa84635 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -346,6 +346,8 @@ static Range GetRange(Compiler* comp, GenTree* tree, BasicBlock* block, ASSERT_V case NI_AVX_MoveMask: case NI_AVX2_MoveMask: case NI_AVX512_MoveMask: +#elif defined(TARGET_WASM) + case NI_PackedSimd_Bitmask: #endif case NI_Vector_ExtractMostSignificantBits: { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index b1209713faf6ef..f23b4b0884aefd 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -23579,11 +23579,8 @@ GenTree* Compiler::gtNewSimdCvtNode( { return gtNewSimdCvtNativeNode(type, fixupVal, simdTargetBaseType, simdSourceBaseType, simdSize); } -#elif defined(TARGET_ARM64) +#elif defined(TARGET_ARM64) || defined(TARGET_WASM) return gtNewSimdCvtNativeNode(type, op1, simdTargetBaseType, simdSourceBaseType, simdSize); -#elif defined(TARGET_WASM) - NYI_WASM_SIMD("gtNewSimdCvtNode"); - return nullptr; #else #error Unsupported platform #endif @@ -23798,7 +23795,39 @@ GenTree* Compiler::gtNewSimdCvtNativeNode( unreached(); } #elif defined(TARGET_WASM) - NYI_WASM_SIMD("gtNewSimdCvtNativeNode"); + NamedIntrinsic intrinsic = NI_Illegal; + + switch (simdTargetBaseType) + { + case TYP_INT: + { + assert(simdSourceBaseType == TYP_FLOAT); + intrinsic = NI_PackedSimd_ConvertToInt32Saturate; + break; + } + + case TYP_UINT: + { + assert(simdSourceBaseType == TYP_FLOAT); + intrinsic = NI_PackedSimd_ConvertToUInt32Saturate; + break; + } + + case TYP_LONG: + case TYP_ULONG: + { + NYI_WASM_SIMD("gtNewSimdCvtNativeNode"); + return nullptr; + } + + default: + { + unreached(); + } + } + + assert(intrinsic != NI_Illegal); + return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdSourceBaseType, simdSize); #else #error Unsupported platform #endif @@ -23877,7 +23906,9 @@ GenTree* Compiler::gtNewSimdCmpOpNode( assert(varTypeIsIntegral(simdBaseType)); assert(!canUseEvexEncodingDebugOnly()); assert((simdSize == 16) || ((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2))); -#endif // TARGET_XARCH +#elif defined(TARGET_WASM) + assert(simdBaseType == TYP_ULONG); +#endif switch (op) { @@ -23933,7 +23964,13 @@ GenTree* Compiler::gtNewSimdCmpOpNode( return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseType, simdSize); } +#endif // TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_WASM) +#if defined(TARGET_WASM) + case GT_GE: + case GT_LE: +#endif case GT_GT: case GT_LT: { @@ -24002,7 +24039,7 @@ GenTree* Compiler::gtNewSimdCmpOpNode( return gtNewSimdCmpOpNode(op, type, op1, op2, simdBaseType, simdSize); } -#endif // TARGET_XARCH +#endif // TARGET_XARCH || TARGET_WASM case GT_NE: { @@ -24183,12 +24220,10 @@ GenTree* Compiler::gtNewSimdCndSelNode( NamedIntrinsic intrinsic = NI_Illegal; -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_WASM) return gtNewSimdHWIntrinsicNode(type, op1, op2, op3, NI_Vector_ConditionalSelect, simdBaseType, simdSize); #elif defined(TARGET_ARM64) return gtNewSimdHWIntrinsicNode(type, op1, op2, op3, NI_AdvSimd_BitwiseSelect, simdBaseType, simdSize); -#elif defined(TARGET_WASM) - return gtNewSimdHWIntrinsicNode(type, op2, op3, op1, NI_PackedSimd_BitwiseSelect, simdBaseType, simdSize); #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 @@ -26148,9 +26183,13 @@ GenTree* Compiler::gtNewSimdMinMaxNode(var_types type, op2 = gtNewSimdCreateScalarUnsafeNode(type, op2, simdBaseType, simdSize); } #elif defined(TARGET_WASM) - NYI_WASM_SIMD("gtNewSimdMinMaxNode"); + if (!isMagnitude && !isNumber) + { + intrinsic = isMax ? NI_PackedSimd_Max : NI_PackedSimd_Min; + retNode = gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseType, simdSize); + } #else - assert(!isScalar); +#error Unsupported platform #endif if (retNode == nullptr) @@ -26251,7 +26290,7 @@ GenTree* Compiler::gtNewSimdMinMaxNode(var_types type, assert(!isScalar); - if (isMagnitude) + if (isMagnitude && !varTypeIsUnsigned(simdBaseType)) { GenTree* op1Dup = fgMakeMultiUse(&op1); GenTree* op2Dup = fgMakeMultiUse(&op2); @@ -26263,7 +26302,7 @@ GenTree* Compiler::gtNewSimdMinMaxNode(var_types type, GenTree* absOp2Dup = fgMakeMultiUse(&absOp2); GenTree* equalsMask = gtNewSimdCmpOpNode(GT_EQ, type, absOp1, absOp2, simdBaseType, simdSize); - ; + GenTree* signMask1 = nullptr; GenTree* signMask2 = nullptr; GenTree* signMask3 = nullptr; @@ -26419,7 +26458,14 @@ GenTree* Compiler::gtNewSimdMinMaxNativeNode( } } #elif defined(TARGET_WASM) - NYI_WASM_SIMD("gtNewSimdMinMaxNode"); + if (varTypeIsFloating(simdBaseType)) + { + intrinsic = isMax ? NI_PackedSimd_PseudoMax : NI_PackedSimd_PseudoMin; + } + else if (!varTypeIsLong(simdBaseType)) + { + intrinsic = isMax ? NI_PackedSimd_Max : NI_PackedSimd_Min; + } #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 @@ -29374,37 +29420,7 @@ GenTree* Compiler::gtNewSimdUnOpNode( #if defined(TARGET_ARM64) if (op == GT_NEG) { - switch (simdBaseType) - { - case TYP_UBYTE: - { - simdBaseType = TYP_BYTE; - break; - } - - case TYP_USHORT: - { - simdBaseType = TYP_SHORT; - break; - } - - case TYP_UINT: - { - simdBaseType = TYP_INT; - break; - } - - case TYP_ULONG: - { - simdBaseType = TYP_LONG; - break; - } - - default: - { - break; - } - } + simdBaseType = varTypeToSigned(simdBaseType); } #endif // TARGET_ARM64 @@ -29463,12 +29479,10 @@ GenTree* Compiler::gtNewSimdWidenLowerNode(var_types type, GenTree* op1, var_typ NamedIntrinsic intrinsic = NI_Illegal; - GenTree* tmp1; - #if defined(TARGET_XARCH) if (simdSize == 64) { - tmp1 = gtNewSimdGetLowerNode(TYP_SIMD32, op1, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdGetLowerNode(TYP_SIMD32, op1, simdBaseType, simdSize); switch (simdBaseType) { @@ -29527,7 +29541,7 @@ GenTree* Compiler::gtNewSimdWidenLowerNode(var_types type, GenTree* op1, var_typ { assert(!varTypeIsIntegral(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); - tmp1 = gtNewSimdGetLowerNode(TYP_SIMD16, op1, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdGetLowerNode(TYP_SIMD16, op1, simdBaseType, simdSize); switch (simdBaseType) { @@ -29608,6 +29622,8 @@ GenTree* Compiler::gtNewSimdWidenLowerNode(var_types type, GenTree* op1, var_typ return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseType, simdSize); } #elif defined(TARGET_ARM64) + GenTree* tmp1; + if (simdSize == 16) { tmp1 = gtNewSimdGetLowerNode(TYP_SIMD8, op1, simdBaseType, simdSize); @@ -29642,9 +29658,22 @@ GenTree* Compiler::gtNewSimdWidenLowerNode(var_types type, GenTree* op1, var_typ return tmp1; #elif defined(TARGET_WASM) - tmp1 = nullptr; - NYI_WASM_SIMD("gtNewSimdWidenLowerNode"); - return nullptr; + if (varTypeIsFloating(simdBaseType)) + { + assert(simdBaseType == TYP_FLOAT); + intrinsic = NI_PackedSimd_ConvertToDoubleLower; + } + else if (varTypeIsSigned(simdBaseType)) + { + intrinsic = NI_PackedSimd_SignExtendWideningLower; + } + else + { + intrinsic = NI_PackedSimd_ZeroExtendWideningLower; + } + + assert(intrinsic != NI_Illegal); + return gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseType, simdSize); #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 @@ -29662,12 +29691,10 @@ GenTree* Compiler::gtNewSimdWidenUpperNode(var_types type, GenTree* op1, var_typ NamedIntrinsic intrinsic = NI_Illegal; - GenTree* tmp1; - #if defined(TARGET_XARCH) if (simdSize == 64) { - tmp1 = gtNewSimdGetUpperNode(TYP_SIMD32, op1, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdGetUpperNode(TYP_SIMD32, op1, simdBaseType, simdSize); switch (simdBaseType) { @@ -29726,7 +29753,7 @@ GenTree* Compiler::gtNewSimdWidenUpperNode(var_types type, GenTree* op1, var_typ { assert(!varTypeIsIntegral(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); - tmp1 = gtNewSimdGetUpperNode(TYP_SIMD16, op1, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdGetUpperNode(TYP_SIMD16, op1, simdBaseType, simdSize); switch (simdBaseType) { @@ -29772,13 +29799,13 @@ GenTree* Compiler::gtNewSimdWidenUpperNode(var_types type, GenTree* op1, var_typ GenTree* op1Dup = fgMakeMultiUse(&op1); - tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op1Dup, NI_X86Base_MoveHighToLow, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdHWIntrinsicNode(type, op1, op1Dup, NI_X86Base_MoveHighToLow, simdBaseType, simdSize); return gtNewSimdHWIntrinsicNode(type, tmp1, NI_X86Base_ConvertToVector128Double, simdBaseType, simdSize); } else { - tmp1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(8), NI_X86Base_ShiftRightLogical128BitLane, - simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(8), NI_X86Base_ShiftRightLogical128BitLane, + simdBaseType, simdSize); switch (simdBaseType) { @@ -29853,13 +29880,26 @@ GenTree* Compiler::gtNewSimdWidenUpperNode(var_types type, GenTree* op1, var_typ assert(intrinsic != NI_Illegal); - tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseType, simdSize); + GenTree* tmp1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseType, simdSize); return gtNewSimdGetUpperNode(TYP_SIMD8, tmp1, simdBaseType, 16); } #elif defined(TARGET_WASM) - tmp1 = nullptr; - NYI_WASM_SIMD("gtNewSimdWidenUpperNode"); - return nullptr; + if (varTypeIsFloating(simdBaseType)) + { + NYI_WASM_SIMD("gtNewSimdWidenUpperNode"); + return nullptr; + } + else if (varTypeIsSigned(simdBaseType)) + { + intrinsic = NI_PackedSimd_SignExtendWideningUpper; + } + else + { + intrinsic = NI_PackedSimd_ZeroExtendWideningUpper; + } + + assert(intrinsic != NI_Illegal); + return gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, intrinsic, simdBaseType, simdSize); #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 @@ -31611,7 +31651,11 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForUnOp( { id = NI_AdvSimd_Negate; } -#endif // TARGET_ARM64 +#elif defined(TARGET_WASM) + id = NI_PackedSimd_Negate; +#elif !defined(TARGET_XARCH) +#error Unsupported platform +#endif break; } @@ -31621,7 +31665,11 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForUnOp( #if defined(TARGET_ARM64) id = NI_AdvSimd_Not; -#endif // TARGET_ARM64 +#elif defined(TARGET_WASM) + id = NI_PackedSimd_Not; +#elif !defined(TARGET_XARCH) +#error Unsupported platform +#endif break; } @@ -32010,10 +32058,7 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForBinOp(Compiler* comp, #elif defined(TARGET_ARM64) id = NI_AdvSimd_Or; #elif defined(TARGET_WASM) - if (!varTypeIsByte(simdBaseType)) - { - id = NI_PackedSimd_Or; - } + id = NI_PackedSimd_Or; #else #error Unsupported platform #endif @@ -32374,7 +32419,11 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = NI_AdvSimd_CompareEqual; } -#endif // !TARGET_XARCH && !TARGET_ARM64 +#elif defined(TARGET_WASM) + id = NI_PackedSimd_CompareEqual; +#else +#error Unsupported platform +#endif break; } @@ -32414,7 +32463,14 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = NI_AdvSimd_CompareGreaterThanOrEqual; } -#endif // !TARGET_XARCH && !TARGET_ARM64 +#elif defined(TARGET_WASM) + if (simdBaseType != TYP_ULONG) + { + id = NI_PackedSimd_CompareGreaterThanOrEqual; + } +#else +#error Unsupported platform +#endif break; } @@ -32468,7 +32524,14 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = NI_AdvSimd_CompareGreaterThan; } -#endif // !TARGET_XARCH && !TARGET_ARM64 +#elif defined(TARGET_WASM) + if (simdBaseType != TYP_ULONG) + { + id = NI_PackedSimd_CompareGreaterThan; + } +#else +#error Unsupported platform +#endif break; } @@ -32508,7 +32571,14 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = NI_AdvSimd_CompareLessThanOrEqual; } -#endif // !TARGET_XARCH && !TARGET_ARM64 +#elif defined(TARGET_WASM) + if (simdBaseType != TYP_ULONG) + { + id = NI_PackedSimd_CompareLessThanOrEqual; + } +#else +#error Unsupported platform +#endif break; } @@ -32564,7 +32634,14 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = NI_AdvSimd_CompareLessThan; } -#endif // !TARGET_XARCH && !TARGET_ARM64 +#elif defined(TARGET_WASM) + if (simdBaseType != TYP_ULONG) + { + id = NI_PackedSimd_CompareLessThan; + } +#else +#error Unsupported platform +#endif break; } @@ -32590,7 +32667,13 @@ NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, { id = isScalar ? NI_X86Base_CompareScalarNotEqual : NI_X86Base_CompareNotEqual; } -#endif // TARGET_XARCH +#elif defined(TARGET_ARM64) + id = NI_Illegal; +#elif defined(TARGET_WASM) + id = NI_PackedSimd_CompareNotEqual; +#else +#error Unsupported platform +#endif break; } @@ -34289,6 +34372,8 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) case NI_X86Base_MoveMask: case NI_AVX_MoveMask: case NI_AVX2_MoveMask: +#elif defined(TARGET_WASM) + case NI_PackedSimd_Bitmask: #endif { simdmask_t simdMaskVal; diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 6cdd21e684f635..421b6adffd2d27 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -2978,12 +2978,6 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, return nullptr; } } -#elif defined(TARGET_WASM) - if (intrinsic != NI_Vector_Create) - { - // TODO-WASM-SIMD: Finish adding xplat intrinsic lightup - return nullptr; - } #endif if (simdSize != 0) @@ -3030,9 +3024,19 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } else { -#if defined(TARGET_XARCH) +#if defined(TARGET_ARM64) + intrinsic = NI_AdvSimd_AddSaturate; + + if ((simdSize == 8) && varTypeIsLong(simdBaseType)) + { + intrinsic = NI_AdvSimd_AddSaturateScalar; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); +#elif defined(TARGET_XARCH) || defined(TARGET_WASM) if (varTypeIsSmall(simdBaseType)) { +#if defined(TARGET_XARCH) if (simdSize == 64) { intrinsic = NI_AVX512_AddSaturate; @@ -3046,7 +3050,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(simdSize == 16); intrinsic = NI_X86Base_AddSaturate; } - +#elif defined(TARGET_WASM) + intrinsic = NI_PackedSimd_AddSaturate; +#else +#error Unsupported platform +#endif retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); } else if (varTypeIsUnsigned(simdBaseType)) @@ -3073,13 +3081,6 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, switch (simdBaseType) { - case TYP_SHORT: - { - minCns->EvaluateBroadcastInPlace(INT16_MIN); - maxCns->EvaluateBroadcastInPlace(INT16_MAX); - break; - } - case TYP_INT: { minCns->EvaluateBroadcastInPlace(INT32_MIN); @@ -3113,6 +3114,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, // The mask we need is ((a ^ b) & ~(b ^ c)) < 0 +#if defined(TARGET_XARCH) if (compOpportunisticallyDependsOn(InstructionSet_AVX512)) { // tmpDup1 = a: 0xF0 @@ -3125,6 +3127,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, simdBaseType, simdSize); } else +#endif { GenTree* op1Dup2 = gtCloneExpr(op1Dup1); @@ -3137,20 +3140,6 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, msk = gtNewSimdIsNegativeNode(retType, msk, simdBaseType, simdSize); retNode = gtNewSimdCndSelNode(retType, msk, ovf, tmpDup2, simdBaseType, simdSize); } -#elif defined(TARGET_ARM64) - intrinsic = NI_AdvSimd_AddSaturate; - - if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - intrinsic = NI_AdvSimd_AddSaturateScalar; - } - - retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); -#elif defined(TARGET_WASM) - // TODO-WASM-SIMD: Implement NI_Vector_AddSaturate - return nullptr; -#else - unreached(); #endif } break; @@ -3187,7 +3176,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, case NI_Vector_AsUInt32: case NI_Vector_AsUInt64: case NI_Vector_AsVector4: -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) || defined(TARGET_WASM) case NI_Vector_AsVector: #endif { @@ -3342,6 +3331,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(simdBaseType == TYP_FLOAT); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_AsVector128(Vector2) - Need WithElement + return nullptr; +#endif + op1 = impSIMDPopStack(); if (op1->IsCnsVec()) @@ -3376,6 +3370,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(simdBaseType == TYP_FLOAT); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_AsVector128(Vector3) - Need WithElement + return nullptr; +#endif + op1 = impSIMDPopStack(); if (op1->IsCnsVec()) @@ -3436,6 +3435,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(simdBaseType == TYP_FLOAT); assert((simdSize == 8) || (simdSize == 12)); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_AsVector128Unsafe + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdHWIntrinsicNode(retType, op1, NI_Vector_AsVector128Unsafe, simdBaseType, simdSize); break; @@ -3447,6 +3451,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert((simdSize == 16) && (simdBaseType == TYP_FLOAT)); assert((retType == TYP_SIMD8) || (retType == TYP_SIMD12)); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_AsVector2/3 + return nullptr; +#endif + assert(sig->numArgs == 1); op1 = impSIMDPopStack(); @@ -3600,6 +3609,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } #endif +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ConvertToInt64 + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNode(retType, op1, TYP_LONG, simdBaseType, simdSize); break; @@ -3622,6 +3636,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } #endif +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ConvertToInt64Native + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNativeNode(retType, op1, TYP_LONG, simdBaseType, simdSize); break; @@ -3672,8 +3691,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, #elif defined(TARGET_ARM64) intrinsic = NI_AdvSimd_ConvertToSingle; #elif defined(TARGET_WASM) - // TODO-WASM-SIMD: Implement NI_Vector_ConvertToSingle - return nullptr; + intrinsic = NI_PackedSimd_ConvertToSingle; #else unreached(); #endif @@ -3734,6 +3752,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } #endif +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ConvertToUInt64 + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNode(retType, op1, TYP_ULONG, simdBaseType, simdSize); break; @@ -3756,6 +3779,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } #endif +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ConvertToUInt64Native + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdCvtNativeNode(retType, op1, TYP_ULONG, simdBaseType, simdSize); break; @@ -3774,6 +3802,14 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, impSpillSideEffect(true, stackState.esStackDepth - 2 DEBUGARG("Spilling op1 side effects for vector CreateAlternatingSequence")); +#if defined(TARGET_WASM) + if (!impStackTop(0).val->OperIsConst() || !impStackTop(1).val->OperIsConst()) + { + // TODO-WASM-SIMD: Implement NI_Vector_CreateAlternatingSequence - Need Shuffle + return nullptr; + } +#endif + op2 = impPopStack().val; op1 = impPopStack().val; @@ -3827,6 +3863,14 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); +#if defined(TARGET_WASM) + if (!impStackTop(0).val->OperIsConst()) + { + // TODO-WASM-SIMD: Implement NI_Vector_CreateScalar + break; + } +#endif + op1 = impPopStack().val; retNode = gtNewSimdCreateScalarNode(retType, op1, simdBaseType, simdSize); break; @@ -3836,6 +3880,14 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); +#if defined(TARGET_WASM) + if (!impStackTop(0).val->OperIsConst()) + { + // TODO-WASM-SIMD: Implement NI_Vector_CreateScalarUnsafe + break; + } +#endif + op1 = impPopStack().val; retNode = gtNewSimdCreateScalarUnsafeNode(retType, op1, simdBaseType, simdSize); break; @@ -3867,6 +3919,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Dot - Need Shuffle + return nullptr; +#endif + #if defined(TARGET_ARM64) if (varTypeIsLong(simdBaseType)) { @@ -3967,6 +4024,21 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, unreached(); } } +#elif defined(TARGET_WASM) + if (simdBaseType == TYP_FLOAT) + { + simdBaseType = TYP_INT; + } + else if (simdBaseType == TYP_DOUBLE) + { + simdBaseType = TYP_LONG; + } + else + { + assert(varTypeIsIntegral(simdBaseType)); + } + + intrinsic = NI_PackedSimd_Bitmask; #endif retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseType, simdSize); @@ -4004,6 +4076,9 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, impSpillSideEffect(true, stackState.esStackDepth - 2 DEBUGARG("Spilling op2 side effects for FusedMultiplyAdd")); +#elif defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_FusedMultiplyAdd + return nullptr; #endif op3 = impSIMDPopStack(); @@ -4018,6 +4093,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_GetElement + return nullptr; +#endif + op2 = impPopStack().val; op1 = impSIMDPopStack(); @@ -4315,10 +4395,10 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) || defined(TARGET_WASM) if (opts.OptimizationDisabled()) { - // ARM64 doesn't have aligned loads, but aligned loads are only validated to be + // ARM64/WASM doesn't have aligned loads, but aligned loads are only validated to be // aligned when optimizations are disable, so only skip the intrinsic handling // if optimizations are enabled break; @@ -4341,10 +4421,10 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) || defined(TARGET_WASM) if (opts.OptimizationDisabled()) { - // ARM64 doesn't have aligned loads, but aligned loads are only validated to be + // ARM64/WASM doesn't have aligned loads, but aligned loads are only validated to be // aligned when optimizations are disable, so only skip the intrinsic handling // if optimizations are enabled break; @@ -4499,6 +4579,8 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { isFmaSupported = compExactlyDependsOn(InstructionSet_AVX2); } +#elif defined(TARGET_WASM) + isFmaSupported = false; #endif if (isFmaSupported) @@ -4517,6 +4599,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Narrow + return nullptr; +#endif + op2 = impSIMDPopStack(); op1 = impSIMDPopStack(); @@ -4777,6 +4864,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 1); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Reverse - Need Shuffle + return nullptr; +#endif + #if defined(TARGET_XARCH) if ((simdSize == 64) && varTypeIsByte(simdBaseType)) { @@ -4821,6 +4913,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, break; } +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ShiftLeft + return nullptr; +#endif + #if defined(TARGET_XARCH) if ((simdSize == 16) && !compOpportunisticallyDependsOn(InstructionSet_AVX2)) { @@ -4868,6 +4965,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert((sig->numArgs == 2) || (sig->numArgs == 3)); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Shuffle + return nullptr; +#endif + bool isShuffleNative = (intrinsic != NI_Vector_Shuffle); bool isNonDeterministic = isShuffleNative; @@ -4949,10 +5051,10 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 2); assert(retType == TYP_VOID); -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) || defined(TARGET_WASM) if (opts.OptimizationDisabled()) { - // ARM64 doesn't have aligned stores, but aligned stores are only validated to be + // ARM64/WASM doesn't have aligned stores, but aligned stores are only validated to be // aligned when optimizations are disable, so only skip the intrinsic handling // if optimizations are enabled break; @@ -4980,10 +5082,10 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 2); assert(retType == TYP_VOID); -#if defined(TARGET_ARM64) +#if defined(TARGET_ARM64) || defined(TARGET_WASM) if (opts.OptimizationDisabled()) { - // ARM64 doesn't have aligned stores, but aligned stores are only validated to be + // ARM64/WASM doesn't have aligned stores, but aligned stores are only validated to be // aligned when optimizations are disable, so only skip the intrinsic handling // if optimizations are enabled break; @@ -5059,9 +5161,19 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, } else { -#if defined(TARGET_XARCH) +#if defined(TARGET_ARM64) + intrinsic = NI_AdvSimd_SubtractSaturate; + + if ((simdSize == 8) && varTypeIsLong(simdBaseType)) + { + intrinsic = NI_AdvSimd_SubtractSaturateScalar; + } + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); +#elif defined(TARGET_XARCH) || defined(TARGET_WASM) if (varTypeIsSmall(simdBaseType)) { +#if defined(TARGET_XARCH) if (simdSize == 64) { intrinsic = NI_AVX512_SubtractSaturate; @@ -5075,6 +5187,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(simdSize == 16); intrinsic = NI_X86Base_SubtractSaturate; } +#elif defined(TARGET_WASM) + intrinsic = NI_PackedSimd_SubtractSaturate; +#else +#error Unsupported platform +#endif retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); } @@ -5102,13 +5219,6 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, switch (simdBaseType) { - case TYP_SHORT: - { - minCns->EvaluateBroadcastInPlace(INT16_MIN); - maxCns->EvaluateBroadcastInPlace(INT16_MAX); - break; - } - case TYP_INT: { minCns->EvaluateBroadcastInPlace(INT32_MIN); @@ -5142,6 +5252,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, // The mask we need is ((a ^ b) & (b ^ c)) < 0 +#if defined(TARGET_XARCH) if (compOpportunisticallyDependsOn(InstructionSet_AVX512)) { // tmpDup1 = a: 0xF0 @@ -5154,6 +5265,7 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, simdBaseType, simdSize); } else +#endif { GenTree* op1Dup2 = gtCloneExpr(op1Dup1); @@ -5166,20 +5278,6 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, msk = gtNewSimdIsNegativeNode(retType, msk, simdBaseType, simdSize); retNode = gtNewSimdCndSelNode(retType, msk, ovf, tmpDup2, simdBaseType, simdSize); } -#elif defined(TARGET_ARM64) - intrinsic = NI_AdvSimd_SubtractSaturate; - - if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - intrinsic = NI_AdvSimd_SubtractSaturateScalar; - } - - retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, intrinsic, simdBaseType, simdSize); -#elif defined(TARGET_WASM) - // TODO-WASM-SIMD: Implement NI_Vector_SubtractSaturate - return nullptr; -#else - unreached(); #endif } break; @@ -5188,6 +5286,12 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, case NI_Vector_Sum: { assert(sig->numArgs == 1); + +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Sum - Need Shuffle + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdSumNode(retType, op1, simdBaseType, simdSize); break; @@ -5196,6 +5300,12 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, case NI_Vector_ToScalar: { assert(sig->numArgs == 1); + +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_ToScalar - Need GetElement + return nullptr; +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdToScalarNode(retType, op1, simdBaseType, simdSize); break; @@ -5221,6 +5331,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Unzip - Need Shuffle + return nullptr; +#endif + #if defined(TARGET_XARCH) if (simdSize == 16) { @@ -5252,6 +5367,15 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, case NI_Vector_WidenUpper: { assert(sig->numArgs == 1); + +#if defined(TARGET_WASM) + if (simdBaseType == TYP_FLOAT) + { + // TODO-WASM-SIMD - Implement NI_Vector_WidenUpper(float) - Need Shuffle + return nullptr; + } +#endif + op1 = impSIMDPopStack(); retNode = gtNewSimdWidenUpperNode(retType, op1, simdBaseType, simdSize); break; @@ -5261,6 +5385,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 3); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_WithElement + return nullptr; +#endif + #if defined(TARGET_X86) if (varTypeIsLong(simdBaseType)) { @@ -5353,6 +5482,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Zip - Need Shuffle + return nullptr; +#endif + op2 = impSIMDPopStack(); op1 = impSIMDPopStack(); @@ -5654,6 +5788,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_op_LeftShift + return nullptr; +#endif + op2 = impPopStack().val; op1 = impSIMDPopStack(); @@ -5691,6 +5830,12 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, case NI_Vector_op_RightShift: { assert(sig->numArgs == 2); + +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_op_RightShift + return nullptr; +#endif + genTreeOps op = varTypeIsUnsigned(simdBaseType) ? GT_RSZ : GT_RSH; op2 = impPopStack().val; @@ -5730,6 +5875,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, { assert(sig->numArgs == 2); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_op_UnsignedRightShift + return nullptr; +#endif + op2 = impPopStack().val; op1 = impSIMDPopStack(); @@ -5772,6 +5922,11 @@ GenTree* Compiler::impXplatIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 2); assert(retNode == nullptr); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_Concat - Need Shuffle + return nullptr; +#endif + op2 = impSIMDPopStack(); op1 = impSIMDPopStack(); diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index cb89bdc1dd10d6..7effb01b75f84b 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -264,7 +264,6 @@ enum HWIntrinsicFlag : uint64_t // The intrinsic supports some sort of containment analysis HW_Flag_SupportsContainment = 0x400, HW_Flag_ReturnsPerElementMask = 0x800, - // TODO-WASM: Add WASM-specific flags as needed. #else #error Unsupported platform #endif diff --git a/src/coreclr/jit/hwintrinsiclist.h b/src/coreclr/jit/hwintrinsiclist.h index d2fd854b9e1309..e9ffeba17c0411 100644 --- a/src/coreclr/jit/hwintrinsiclist.h +++ b/src/coreclr/jit/hwintrinsiclist.h @@ -107,7 +107,7 @@ HARDWARE_INTRINSIC(Vector, CreateScalar, HARDWARE_INTRINSIC(Vector, CreateScalarUnsafe, -1, 1, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_ins, INS_fmov, INS_fmov, -1, -1, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_SupportsContainment) #else HARDWARE_INTRINSIC(Vector, CreateScalar, -1, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(Vector, CreateScalarUnsafe, -1, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector, CreateScalarUnsafe, -1, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen) #endif HARDWARE_INTRINSIC(Vector, CreateSequence, -1, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_AvxOnlyCompatible) diff --git a/src/coreclr/jit/hwintrinsiclistwasm.h b/src/coreclr/jit/hwintrinsiclistwasm.h index d728e7da3f8e00..ae0eb686c75edd 100644 --- a/src/coreclr/jit/hwintrinsiclistwasm.h +++ b/src/coreclr/jit/hwintrinsiclistwasm.h @@ -29,11 +29,10 @@ HARDWARE_INTRINSIC(PackedSimd, Bitmask, HARDWARE_INTRINSIC(PackedSimd, BitwiseSelect, 16, 3, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, INS_v128_bitselect, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, Ceiling, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_ceil, INS_f64x2_ceil, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, CompareEqual, 16, 2, INS_i8x16_eq, INS_i8x16_eq, INS_i16x8_eq, INS_i16x8_eq, INS_i32x4_eq, INS_i32x4_eq, INS_i64x2_eq, INS_i64x2_eq, INS_f32x4_eq, INS_f64x2_eq, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative|HW_Flag_ReturnsPerElementMask) -// TODO-WASM-SIMD: ULONG slot below is INS_invalid because the WASM SIMD spec has no unsigned i64x2 compare (lt_u/le_u/gt_u/ge_u). Requires special codegen (e.g., XOR sign bit + signed compare) to support Vector128 ordered compares. Until that lands, route through impSpecialIntrinsic via HW_Flag_InvalidNodeId so calls fail with a clear NYI instead of recursing into the self-referential managed bodies in PackedSimd.cs. -HARDWARE_INTRINSIC(PackedSimd, CompareGreaterThan, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, CompareGreaterThanOrEqual, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, CompareLessThan, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, CompareLessThanOrEqual, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, CompareGreaterThan, 16, 2, INS_i8x16_gt_s, INS_i8x16_gt_u, INS_i16x8_gt_s, INS_i16x8_gt_u, INS_i32x4_gt_s, INS_i32x4_gt_u, INS_i64x2_gt_s, INS_invalid, INS_f32x4_gt, INS_f64x2_gt, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(PackedSimd, CompareGreaterThanOrEqual, 16, 2, INS_i8x16_ge_s, INS_i8x16_ge_u, INS_i16x8_ge_s, INS_i16x8_ge_u, INS_i32x4_ge_s, INS_i32x4_ge_u, INS_i64x2_ge_s, INS_invalid, INS_f32x4_ge, INS_f64x2_ge, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(PackedSimd, CompareLessThan, 16, 2, INS_i8x16_lt_s, INS_i8x16_lt_u, INS_i16x8_lt_s, INS_i16x8_lt_u, INS_i32x4_lt_s, INS_i32x4_lt_u, INS_i64x2_lt_s, INS_invalid, INS_f32x4_lt, INS_f64x2_lt, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(PackedSimd, CompareLessThanOrEqual, 16, 2, INS_i8x16_le_s, INS_i8x16_le_u, INS_i16x8_le_s, INS_i16x8_le_u, INS_i32x4_le_s, INS_i32x4_le_u, INS_i64x2_le_s, INS_invalid, INS_f32x4_le, INS_f64x2_le, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(PackedSimd, CompareNotEqual, 16, 2, INS_i8x16_ne, INS_i8x16_ne, INS_i16x8_ne, INS_i16x8_ne, INS_i32x4_ne, INS_i32x4_ne, INS_i64x2_ne, INS_i64x2_ne, INS_f32x4_ne, INS_f64x2_ne, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(PackedSimd, ConvertNarrowingSaturateSigned, 16, 2, INS_invalid, INS_invalid, INS_i8x16_narrow_i16x8_s, INS_invalid, INS_i16x8_narrow_i32x4_s, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, ConvertNarrowingSaturateUnsigned, 16, 2, INS_invalid, INS_invalid, INS_i8x16_narrow_i16x8_u, INS_invalid, INS_i16x8_narrow_i32x4_u, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) @@ -43,13 +42,13 @@ HARDWARE_INTRINSIC(PackedSimd, ConvertToSingle, HARDWARE_INTRINSIC(PackedSimd, ConvertToUInt32Saturate, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_i32x4_trunc_sat_u_f32x4, INS_i32x4_trunc_sat_u_f64x2_zero, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, Divide, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_div, INS_f64x2_div, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, Dot, 16, 2, INS_invalid, INS_invalid, INS_i32x4_dot_i16x8_s, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, ExtractScalar, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, ExtractScalar, 16, 2, INS_i8x16_extract_lane_s, INS_i8x16_extract_lane_u, INS_i16x8_extract_lane_s, INS_i16x8_extract_lane_u, INS_i32x4_extract_lane, INS_i32x4_extract_lane, INS_i64x2_extract_lane, INS_i64x2_extract_lane, INS_f32x4_extract_lane, INS_f64x2_extract_lane, -1, -1, HW_Category_IMM, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport) HARDWARE_INTRINSIC(PackedSimd, Floor, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_floor, INS_f64x2_floor, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, LoadScalarAndInsert, 16, 3, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, LoadScalarAndSplatVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, LoadScalarVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, LoadScalarAndInsert, 16, 3, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(PackedSimd, LoadScalarAndSplatVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(PackedSimd, LoadScalarVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport) HARDWARE_INTRINSIC(PackedSimd, LoadVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport) -HARDWARE_INTRINSIC(PackedSimd, LoadWideningVector128, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, LoadWideningVector128, 16, 1, INS_v128_load8x8_s, INS_v128_load8x8_u, INS_v128_load16x4_s, INS_v128_load16x4_u, INS_v128_load32x2_s, INS_v128_load32x2_u, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_MemoryLoad, HW_Flag_SpecialImport) HARDWARE_INTRINSIC(PackedSimd, Max, 16, 2, INS_i8x16_max_s, INS_i8x16_max_u, INS_i16x8_max_s, INS_i16x8_max_u, INS_i32x4_max_s, INS_i32x4_max_u, INS_invalid, INS_invalid, INS_f32x4_max, INS_f64x2_max, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) HARDWARE_INTRINSIC(PackedSimd, Min, 16, 2, INS_i8x16_min_s, INS_i8x16_min_u, INS_i16x8_min_s, INS_i16x8_min_u, INS_i32x4_min_s, INS_i32x4_min_u, INS_invalid, INS_invalid, INS_f32x4_min, INS_f64x2_min, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) HARDWARE_INTRINSIC(PackedSimd, Multiply, 16, 2, INS_invalid, INS_invalid, INS_i16x8_mul, INS_i16x8_mul, INS_i32x4_mul, INS_i32x4_mul, INS_i64x2_mul, INS_i64x2_mul, INS_f32x4_mul, INS_f64x2_mul, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) @@ -62,17 +61,18 @@ HARDWARE_INTRINSIC(PackedSimd, Or, HARDWARE_INTRINSIC(PackedSimd, PopCount, 16, 1, INS_invalid, INS_i8x16_popcnt, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, PseudoMax, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_pmax, INS_f64x2_pmax, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, PseudoMin, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_pmin, INS_f64x2_pmin, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, ReplaceScalar, 16, 3, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, ReplaceScalar, 16, 3, INS_i8x16_replace_lane, INS_i8x16_replace_lane, INS_i16x8_replace_lane, INS_i16x8_replace_lane, INS_i32x4_replace_lane, INS_i32x4_replace_lane, INS_i64x2_replace_lane, INS_i64x2_replace_lane, INS_f32x4_replace_lane, INS_f64x2_replace_lane, -1, -1, HW_Category_IMM, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport) HARDWARE_INTRINSIC(PackedSimd, RoundToNearest, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_nearest, INS_f64x2_nearest, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, ShiftLeft, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, ShiftRightArithmetic, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(PackedSimd, ShiftRightLogical, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, ShiftLeft, 16, 2, INS_i8x16_shl, INS_i8x16_shl, INS_i16x8_shl, INS_i16x8_shl, INS_i32x4_shl, INS_i32x4_shl, INS_i64x2_shl, INS_i64x2_shl, INS_invalid, INS_invalid, -1, -1, HW_Category_IMM, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(PackedSimd, ShiftRightArithmetic, 16, 2, INS_i8x16_shr_s, INS_i8x16_shr_s, INS_i16x8_shr_s, INS_i16x8_shr_s, INS_i32x4_shr_s, INS_i32x4_shr_s, INS_i64x2_shr_s, INS_i64x2_shr_s, INS_invalid, INS_invalid, -1, -1, HW_Category_IMM, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(PackedSimd, ShiftRightLogical, 16, 2, INS_i8x16_shr_u, INS_i8x16_shr_u, INS_i16x8_shr_u, INS_i16x8_shr_u, INS_i32x4_shr_u, INS_i32x4_shr_u, INS_i64x2_shr_u, INS_i64x2_shr_u, INS_invalid, INS_invalid, -1, -1, HW_Category_IMM, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialImport) +HARDWARE_INTRINSIC(PackedSimd, Shuffle, 16, 3, INS_i8x16_shuffle, INS_i8x16_shuffle, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, SignExtendWideningLower, 16, 1, INS_i16x8_extend_low_s_i8x16, INS_i16x8_extend_low_s_i8x16, INS_i32x4_extend_low_s_i16x8, INS_i32x4_extend_low_s_i16x8, INS_i64x2_extend_low_s_i32x4, INS_i64x2_extend_low_s_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, SignExtendWideningUpper, 16, 1, INS_i16x8_extend_high_s_i8x16, INS_i16x8_extend_high_s_i8x16, INS_i32x4_extend_high_s_i16x8, INS_i32x4_extend_high_s_i16x8, INS_i64x2_extend_high_s_i32x4, INS_i64x2_extend_high_s_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, Splat, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, Splat, 16, 1, INS_i8x16_splat, INS_i8x16_splat, INS_i16x8_splat, INS_i16x8_splat, INS_i32x4_splat, INS_i32x4_splat, INS_i64x2_splat, INS_i64x2_splat, INS_f32x4_splat, INS_f64x2_splat, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, Sqrt, 16, 1, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_sqrt, INS_f64x2_sqrt, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(PackedSimd, Store, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_MemoryStore, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg) -HARDWARE_INTRINSIC(PackedSimd, StoreSelectedScalar, 16, 3, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(PackedSimd, Store, 16, 2, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg) +HARDWARE_INTRINSIC(PackedSimd, StoreSelectedScalar, 16, 3, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_Helper, HW_Flag_InvalidNodeId|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg) HARDWARE_INTRINSIC(PackedSimd, Subtract, 16, 2, INS_i8x16_sub, INS_i8x16_sub, INS_i16x8_sub, INS_i16x8_sub, INS_i32x4_sub, INS_i32x4_sub, INS_i64x2_sub, INS_i64x2_sub, INS_f32x4_sub, INS_f64x2_sub, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, SubtractSaturate, 16, 2, INS_i8x16_sub_sat_s, INS_i8x16_sub_sat_u, INS_i16x8_sub_sat_s, INS_i16x8_sub_sat_u, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(PackedSimd, Swizzle, 16, 2, INS_i8x16_swizzle, INS_i8x16_swizzle, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, -1, -1, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) diff --git a/src/coreclr/jit/hwintrinsicwasm.cpp b/src/coreclr/jit/hwintrinsicwasm.cpp index 25ab03cdb2f9b0..05d20b8f5f37ee 100644 --- a/src/coreclr/jit/hwintrinsicwasm.cpp +++ b/src/coreclr/jit/hwintrinsicwasm.cpp @@ -92,44 +92,103 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, switch (intrinsic) { - // The following PackedSimd intrinsics are not yet implemented on WASM. Because they are must-expand, - // when we return nullptr here the importer will insert a PlatformNotSupportedException throw. case NI_PackedSimd_CompareGreaterThan: + { + assert(sig->numArgs == 2); + assert(simdSize == 16); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + retNode = gtNewSimdCmpOpNode(GT_GT, retType, op1, op2, simdBaseType, simdSize); + break; + } + case NI_PackedSimd_CompareGreaterThanOrEqual: + { + assert(sig->numArgs == 2); + assert(simdSize == 16); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + retNode = gtNewSimdCmpOpNode(GT_GE, retType, op1, op2, simdBaseType, simdSize); + break; + } + case NI_PackedSimd_CompareLessThan: + { + assert(sig->numArgs == 2); + assert(simdSize == 16); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + retNode = gtNewSimdCmpOpNode(GT_LT, retType, op1, op2, simdBaseType, simdSize); + break; + } + case NI_PackedSimd_CompareLessThanOrEqual: + { + assert(sig->numArgs == 2); + assert(simdSize == 16); + + op2 = impSIMDPopStack(); + op1 = impSIMDPopStack(); + + retNode = gtNewSimdCmpOpNode(GT_LE, retType, op1, op2, simdBaseType, simdSize); break; + } + // The following PackedSimd intrinsics are not yet implemented on WASM. Because they are must-expand, + // when we return nullptr here the importer will insert a PlatformNotSupportedException throw. case NI_PackedSimd_ExtractScalar: + { break; + } case NI_PackedSimd_ReplaceScalar: + { break; + } case NI_PackedSimd_LoadVector128: { assert(sig->numArgs == 1); assert(simdSize == 16); + op1 = impPopStack().val; + if (op1->OperIs(GT_CAST) && op1->gtGetOp1()->TypeIs(TYP_BYREF)) { // If what we have is a BYREF, that's what we really want, so throw away the cast. op1 = op1->gtGetOp1(); } - return gtNewSimdLoadNode(retType, op1, simdBaseType, simdSize); + retNode = gtNewSimdLoadNode(retType, op1, simdBaseType, simdSize); + break; } case NI_PackedSimd_LoadScalarVector128: case NI_PackedSimd_LoadScalarAndSplatVector128: case NI_PackedSimd_LoadScalarAndInsert: case NI_PackedSimd_LoadWideningVector128: + { break; + } + + case NI_PackedSimd_ShiftLeft: + case NI_PackedSimd_ShiftRightArithmetic: + case NI_PackedSimd_ShiftRightLogical: + { + break; + } case NI_PackedSimd_Store: { assert(sig->numArgs == 2); assert(simdSize == 16); + op2 = impPopStack().val; op1 = impPopStack().val; @@ -139,23 +198,18 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, op1 = op1->gtGetOp1(); } - return gtNewSimdStoreNode(op1, op2, simdBaseType, simdSize); + retNode = gtNewSimdStoreNode(op1, op2, simdBaseType, simdSize); + break; } case NI_PackedSimd_StoreSelectedScalar: + { break; - - case NI_PackedSimd_ShiftLeft: - case NI_PackedSimd_ShiftRightArithmetic: - case NI_PackedSimd_ShiftRightLogical: - break; - - case NI_PackedSimd_Splat: - break; + } default: { - break; + unreached(); } } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index e5460c6d4b4a72..61db69f06ddb25 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -5282,19 +5282,29 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, else if (!isNative || !BlockNonDeterministicIntrinsics(mustExpand)) { #if defined(FEATURE_HW_INTRINSICS) +#if !defined(TARGET_WASM) GenTree* op2 = impImplicitR4orR8Cast(impPopStack().val, callType); GenTree* op1 = impImplicitR4orR8Cast(impPopStack().val, callType); +#endif if (isNative) { assert(!isMagnitude && !isNumber); +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_MinMax - Need GetElement +#else retNode = gtNewSimdMinMaxNativeNode(callType, op1, op2, JitType2PreciseVarType(callJitType), 0, isMax); +#endif } else { +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Implement NI_Vector_MinMax - Need GetElement +#else retNode = gtNewSimdMinMaxNode(callType, op1, op2, JitType2PreciseVarType(callJitType), 0, isMax, isMagnitude, isNumber); +#endif } #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 426a284166868d..0a7e8e85e17115 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1960,6 +1960,11 @@ class LocalAddressVisitor final : public GenTreeVisitor return IndirTransform::LclFld; } +#if defined(TARGET_WASM) + // TODO-WASM-SIMD: Handle once GetElement and WithElement are supported + return IndirTransform::LclFld; +#endif + #ifdef FEATURE_HW_INTRINSICS if (varTypeIsSIMD(varDsc)) { diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 7c6ddf4c701955..124a233e37cb3f 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -825,19 +825,276 @@ void Lowering::AfterLowerArgsForCall(GenTreeCall* call) // GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) { - HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(node->GetHWIntrinsicId()); - switch (category) + NamedIntrinsic intrinsic = node->GetHWIntrinsicId(); + HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); + + switch (intrinsic) { - case HWIntrinsicCategory::HW_Category_SIMD: + case NI_Vector_ConditionalSelect: { - break; + return LowerHWIntrinsicCndSel(node); } + + case NI_Vector_Create: + case NI_Vector_CreateScalar: + { + return LowerHWIntrinsicCreate(node); + } + + case NI_Vector_op_Equality: + { + assert(category == HW_Category_Helper); + return LowerHWIntrinsicCmpOp(node, GT_EQ); + } + + case NI_Vector_op_Inequality: + { + assert(category == HW_Category_Helper); + return LowerHWIntrinsicCmpOp(node, GT_NE); + } + default: - NYI_WASM_SIMD("Lowering::LowerHWIntrinsic"); + { + assert(category == HW_Category_SIMD); + break; + } } return node->gtNext; } +//---------------------------------------------------------------------------------------------- +// Lowering::LowerHWIntrinsicCmpOp: Lowers a Vector128 comparison intrinsic +// +// Arguments: +// node - The hardware intrinsic node. +// cmpOp - The comparison operation, currently must be GT_EQ or GT_NE +// +GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cmpOp) +{ + NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); + var_types simdBaseType = node->GetSimdBaseType(); + unsigned simdSize = node->GetSimdSize(); + var_types simdType = Compiler::getSIMDTypeForSize(simdSize); + + assert((intrinsicId == NI_Vector_op_Equality) || (intrinsicId == NI_Vector_op_Inequality)); + + assert(varTypeIsSIMD(simdType)); + assert(varTypeIsArithmetic(simdBaseType)); + assert(simdSize != 0); + assert(node->TypeIs(TYP_INT)); + assert((cmpOp == GT_EQ) || (cmpOp == GT_NE)); + + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + + NamedIntrinsic compareIntrinsic = NI_PackedSimd_CompareEqual; + NamedIntrinsic reduceIntrinsic = NI_PackedSimd_AllTrue; + + if (intrinsicId == NI_Vector_op_Inequality) + { + compareIntrinsic = NI_PackedSimd_CompareNotEqual; + reduceIntrinsic = NI_PackedSimd_AnyTrue; + } + + GenTree* cmp = m_compiler->gtNewSimdHWIntrinsicNode(simdType, op1, op2, compareIntrinsic, simdBaseType, simdSize); + BlockRange().InsertBefore(node, cmp); + LowerNode(cmp); + + node->gtType = TYP_INT; + node->ResetHWIntrinsicId(reduceIntrinsic, m_compiler, cmp); + + if (simdBaseType == TYP_FLOAT) + { + node->SetSimdBaseType(TYP_INT); + } + else if (simdBaseType == TYP_DOUBLE) + { + node->SetSimdBaseType(TYP_LONG); + } + else + { + assert(varTypeIsIntegral(simdBaseType)); + } + + return LowerNode(node); +} + +//---------------------------------------------------------------------------------------------- +// Lowering::LowerHWIntrinsicCndSel: Lowers a Vector128 ConditionalSelect call +// +// Arguments: +// node - The hardware intrinsic node. +// +GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* node) +{ + var_types simdType = node->gtType; + var_types simdBaseType = node->GetSimdBaseType(); + unsigned simdSize = node->GetSimdSize(); + + assert(varTypeIsSIMD(simdType)); + assert(varTypeIsArithmetic(simdBaseType)); + assert(simdSize != 0); + + // Get the three arguments to ConditionalSelect we stored in node + // op1: the condition vector + // op2: the left vector + // op3: the right vector + + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + GenTree* op3 = node->Op(3); + + if (op3->IsVectorZero()) + { + // The operation is (op2 & op1) | (zero & ~op1), so we can drop the second half + BlockRange().Remove(op3); + node->ResetHWIntrinsicId(NI_PackedSimd_And, m_compiler, op1, op2); + } + else if (op2->IsVectorZero()) + { + // The operation is (zero & op1) | (op3 & ~op1), so we can drop the first half + BlockRange().Remove(op2); + node->ResetHWIntrinsicId(NI_PackedSimd_AndNot, m_compiler, op3, op1); + } + else + { + // PackedSimd.BitwiseSelect is (left, right, condition) + node->ResetHWIntrinsicId(NI_PackedSimd_BitwiseSelect, m_compiler, op2, op3, op1); + } + return LowerNode(node); +} + +//---------------------------------------------------------------------------------------------- +// Lowering::LowerHWIntrinsicCreate: Lowers a Vector128 Create call +// +// Arguments: +// node - The hardware intrinsic node. +// +GenTree* Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node) +{ + NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); + var_types simdType = node->TypeGet(); + var_types simdBaseType = node->GetSimdBaseType(); + unsigned simdSize = node->GetSimdSize(); + simd_t simdVal = {}; + + assert(varTypeIsSIMD(simdType)); + assert(varTypeIsArithmetic(simdBaseType)); + assert(simdSize != 0); + + bool isConstant = GenTreeVecCon::IsHWIntrinsicCreateConstant(node, simdVal); + bool isCreateScalar = HWIntrinsicInfo::IsVectorCreateScalar(intrinsicId); + size_t argCnt = node->GetOperandCount(); + + if (isConstant) + { + for (GenTree* arg : node->Operands()) + { + BlockRange().Remove(arg); + } + + GenTreeVecCon* vecCon = m_compiler->gtNewVconNode(simdType); + + vecCon->gtSimdVal = simdVal; + BlockRange().InsertBefore(node, vecCon); + + LIR::Use use; + if (BlockRange().TryGetUse(node, &use)) + { + use.ReplaceWith(vecCon); + } + else + { + vecCon->SetUnusedValue(); + } + + BlockRange().Remove(node); + + return LowerNode(vecCon); + } + else if (argCnt == 1) + { + if (isCreateScalar) + { + GenTree* op1 = node->Op(1); + + GenTree* tmp = m_compiler->gtNewZeroConNode(simdType); + BlockRange().InsertBefore(op1, tmp); + LowerNode(tmp); + + GenTree* idx = m_compiler->gtNewIconNode(0); + BlockRange().InsertAfter(tmp, idx); + LowerNode(idx); + + node->ResetHWIntrinsicId(NI_PackedSimd_ReplaceScalar, m_compiler, tmp, idx, op1); + return LowerNode(node); + } + + node->ChangeHWIntrinsicId(NI_PackedSimd_Splat); + return LowerNode(node); + } + + // We have the following (where simd is simd8 or simd16): + // /--* op1 T + // +--* ... T + // +--* opN T + // node = * HWINTRINSIC simd T Create + + // We will be constructing the following parts: + // /--* op1 T + // tmp1 = * HWINTRINSIC simd16 T CreateScalarUnsafe + // ... + + // This is roughly the following managed code: + // var tmp1 = Vector128.CreateScalarUnsafe(op1); + // ... + + GenTree* tmp1 = InsertNewSimdCreateScalarUnsafeNode(simdType, node->Op(1), simdBaseType, simdSize); + LowerNode(tmp1); + + // We will be constructing the following parts: + // ... + // idx = CNS_INT int N + // /--* tmp1 simd + // +--* idx int + // +--* opN T + // tmp1 = * HWINTRINSIC simd T Insert + // ... + + // This is roughly the following managed code: + // ... + // tmp1 = PackedSimd.ReplaceScalar(tmp1, N, opN); + // ... + + unsigned N = 0; + GenTree* opN = nullptr; + GenTree* idx = nullptr; + + for (N = 1; N < argCnt - 1; N++) + { + opN = node->Op(N + 1); + + // Place the insert as early as possible to avoid creating a lot of long lifetimes. + GenTree* insertionPoint = LIR::LastNode(tmp1, opN); + idx = m_compiler->gtNewIconNode(N); + tmp1 = m_compiler->gtNewSimdHWIntrinsicNode(simdType, tmp1, idx, opN, NI_PackedSimd_ReplaceScalar, simdBaseType, + simdSize); + BlockRange().InsertAfter(insertionPoint, idx, tmp1); + LowerNode(tmp1); + } + + assert(N == (argCnt - 1)); + + // For the last insert, we will reuse the existing node and so handle it here, outside the loop. + opN = node->Op(argCnt); + idx = m_compiler->gtNewIconNode(N); + BlockRange().InsertBefore(opN, idx); + + node->ResetHWIntrinsicId(NI_PackedSimd_ReplaceScalar, m_compiler, tmp1, idx, opN); + + return LowerNode(node); +} + //---------------------------------------------------------------------------------------------- // ContainCheckHWIntrinsic: Perform containment analysis for a hardware intrinsic node. // diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 769e7170e6bb66..cff6a6e66e85f1 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8291,6 +8291,8 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunUnary(GenTreeHWIntrinsic* tree, case NI_X86Base_MoveMask: case NI_AVX_MoveMask: case NI_AVX2_MoveMask: +#elif defined(TARGET_WASM) + case NI_PackedSimd_Bitmask: #endif {