diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index f8f9eddf0c6322..24a8020f9ba569 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -796,6 +796,9 @@ class CodeGen final : public CodeGenInterface #if defined(TARGET_WASM) void genCodeForConstant(GenTree* treeNode); +#if defined(FEATURE_SIMD) + void genCodeForVectorConstant(GenTree* treeNode); +#endif void genCatchArg(GenTree* treeNode); #endif diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 7a0bbb5efeb440..690e90b0c0cea0 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -828,6 +828,14 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) return; } +#ifdef FEATURE_HW_INTRINSICS + if (treeNode->OperIsHWIntrinsic()) + { + genHWIntrinsic(treeNode->AsHWIntrinsic()); + return; + } +#endif // FEATURE_HW_INTRINSICS + switch (treeNode->OperGet()) { case GT_ADD: @@ -1004,6 +1012,12 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCkfinite(treeNode); break; +#if defined(FEATURE_SIMD) + case GT_CNS_VEC: + genCodeForVectorConstant(treeNode); + break; +#endif // FEATURE_SIMD + default: #ifdef DEBUG if (JitConfig.JitWasmNyiToR2RUnsupported()) @@ -1883,6 +1897,22 @@ void CodeGen::genCodeForConstant(GenTree* treeNode) WasmProduceReg(treeNode); } +#ifdef FEATURE_SIMD +void CodeGen::genCodeForVectorConstant(GenTree* treeNode) +{ + assert(treeNode->IsCnsVec()); + GenTreeVecCon* vecCon = treeNode->AsVecCon(); + + uint8_t bytes[16] = {}; + memcpy(bytes, &vecCon->gtSimdVal, genTypeSize(vecCon->TypeGet())); + + // There is only one type variant for v128.const, v128.const + // and the bytes are reinterpreted according to whichever operation consumes the value. + GetEmitter()->emitIns_V128Imm(INS_v128_const, bytes); + WasmProduceReg(treeNode); +} +#endif + //------------------------------------------------------------------------ // genCodeForShift: Generate code for a shift or rotate operator // @@ -2553,10 +2583,6 @@ void CodeGen::genCodeForLclVar(GenTreeLclVar* tree) if (!varDsc->lvIsRegCandidate()) { var_types type = varDsc->GetRegisterType(tree); - if (type == TYP_SIMD16) - { - NYI_WASM_SIMD("SIMD16 local load"); - } GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex()); GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(type), tree->GetLclNum(), 0); @@ -2642,13 +2668,8 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree) { assert(tree->OperIs(GT_IND)); - var_types type = tree->TypeGet(); - if (type == TYP_SIMD16) - { - NYI_WASM_SIMD("SIMD16 indirect load"); - } - - instruction ins = ins_Load(type); + var_types type = tree->TypeGet(); + instruction ins = ins_Load(type); genConsumeAddress(tree->Addr()); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c992f86b585568..105ed99fe35427 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2024,6 +2024,13 @@ void Compiler::compSetProcessor() // Add virtual vector ISAs. These are both supported as part of the required baseline. instructionSetFlags.AddInstructionSet(InstructionSet_Vector64); instructionSetFlags.AddInstructionSet(InstructionSet_Vector128); +#elif defined(TARGET_WASM) + // Ensure required baseline ISAs are supported in JIT code, even if not passed in by the VM. + instructionSetFlags.AddInstructionSet(InstructionSet_WasmBase); + instructionSetFlags.AddInstructionSet(InstructionSet_PackedSimd); + + // Add virtual vector ISA. Vector128 is part of the required Wasm SIMD baseline. + instructionSetFlags.AddInstructionSet(InstructionSet_Vector128); #endif // TARGET_ARM64 assert(instructionSetFlags.Equals(EnsureInstructionSetFlagsAreValid(instructionSetFlags))); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 15161eacd678d8..9c1ab063bddb6b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5152,6 +5152,11 @@ class Compiler GenTree* impImportLdvirtftn(GenTree* thisPtr, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); #if defined(FEATURE_HW_INTRINSICS) + GenTree* impSimdCreate(NamedIntrinsic intrinsic, + CORINFO_SIG_INFO* sig, + var_types simdBaseType, + var_types retType, + unsigned simdSize); GenTree* impSimdCreateScalarHalf(GenTree* op1); GenTree* impSimdToScalarHalf(GenTree* op1, CORINFO_CLASS_HANDLE halfClsHnd); #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 9adc0f618894bb..b5233482ee10f8 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -4777,6 +4777,13 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp) // We want the more complex tree to be evaluated first. if ((level < lvl2) && !multiOp->AsHWIntrinsic()->IsUserCall() && gtCanSwapOrder(op1, op2)) { +#if defined(TARGET_WASM) + // TODO-WASM-CQ: Wasm's stack-machine codegen requires operands to be evaluated in + // source order, so we cannot honor GTF_REVERSE_OPS here. For commutative HW + // intrinsics we could instead physically swap multiOp->Op(1)/Op(2) to retain the + // CQ benefit (mirroring the binary commutative path in gtSetEvalOrder). For now, + // just skip the swap to keep evaluation order intact. +#else if (multiOp->IsReverseOp()) { multiOp->ClearReverseOp(); @@ -4787,6 +4794,7 @@ unsigned Compiler::gtSetMultiOpOrder(GenTreeMultiOp* multiOp) } std::swap(level, lvl2); +#endif // TARGET_WASM } if (level < 1) diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 9ac8e28620bf92..60466cef97decd 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1048,9 +1048,8 @@ static const HWIntrinsicIsaRange hwintrinsicIsaRangeArray[] = { { NI_Illegal, NI_Illegal }, // SveSm4_Arm64 #elif defined(TARGET_WASM) { NI_Illegal, NI_Illegal }, // WasmBase - { NI_Illegal, NI_Illegal }, // PackedSimd + { FIRST_NI_PackedSimd, LAST_NI_PackedSimd }, // PackedSimd { FIRST_NI_Vector128, LAST_NI_Vector128 }, // Vector128 - // TODO-WASM: Add PackedSimd intrinsic ranges #else #error Unsupported platform #endif @@ -1605,6 +1604,173 @@ GenTree* Compiler::getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE return arg; } +//------------------------------------------------------------------------ +// impSimdCreate: Common importer logic for Vector{64,128,256,512}.Create intrinsics. +// +// Handles the shared pattern used across xarch, arm64, and wasm: +// * sig->numArgs == 1 -> emit a broadcast/splat node +// * sig->numArgs == simdLength, all const-> fold into a single GenTreeVecCon +// * otherwise -> pack operands into a GT_HWINTRINSIC +// via IntrinsicNodeBuilder +// +// Arguments: +// intrinsic -- the NI_Vector*_Create NamedIntrinsic being imported +// sig -- the call signature (used for numArgs) +// simdBaseType -- the base element type of the SIMD vector +// retType -- the SIMD return type +// simdSize -- size in bytes of the SIMD vector +// +// Return Value: +// The imported GenTree* representing the Create call. Operands are popped +// off the importer stack as part of this call. +// +GenTree* Compiler::impSimdCreate( + NamedIntrinsic intrinsic, CORINFO_SIG_INFO* sig, var_types simdBaseType, var_types retType, unsigned simdSize) +{ + if (sig->numArgs == 1) + { + GenTree* op1 = impStackTop().val; + +#ifdef TARGET_WASM + if (!(op1->IsIntegralConst() || op1->IsCnsFltOrDbl())) + { + // Vector*.Create(T) with a non-constant operand is not yet supported on Wasm. + // Returning nullptr lets the importer fall back to the managed implementation. + return nullptr; + } +#endif + + op1 = impPopStack().val; + return gtNewSimdCreateBroadcastNode(retType, op1, simdBaseType, simdSize); + } + + uint32_t simdLength = getSIMDVectorLength(simdSize, simdBaseType); + assert(sig->numArgs == simdLength); + + bool isConstant = true; + + if (varTypeIsFloating(simdBaseType)) + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + if (!impStackTop(index).val->IsCnsFltOrDbl()) + { + isConstant = false; + break; + } + } + } + else + { + assert(varTypeIsIntegral(simdBaseType)); + + for (uint32_t index = 0; index < sig->numArgs; index++) + { + if (!impStackTop(index).val->IsIntegralConst()) + { + isConstant = false; + break; + } + } + } + + if (isConstant) + { + GenTreeVecCon* vecCon = gtNewVconNode(retType); + + switch (simdBaseType) + { + case TYP_BYTE: + case TYP_UBYTE: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + uint8_t cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); + vecCon->gtSimdVal.u8[simdLength - 1 - index] = cnsVal; + } + break; + } + + case TYP_SHORT: + case TYP_USHORT: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + uint16_t cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); + vecCon->gtSimdVal.u16[simdLength - 1 - index] = cnsVal; + } + break; + } + + case TYP_INT: + case TYP_UINT: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + uint32_t cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); + vecCon->gtSimdVal.u32[simdLength - 1 - index] = cnsVal; + } + break; + } + + case TYP_LONG: + case TYP_ULONG: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + uint64_t cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); + vecCon->gtSimdVal.u64[simdLength - 1 - index] = cnsVal; + } + break; + } + + case TYP_FLOAT: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + float cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); + vecCon->gtSimdVal.f32[simdLength - 1 - index] = cnsVal; + } + break; + } + + case TYP_DOUBLE: + { + for (uint32_t index = 0; index < sig->numArgs; index++) + { + double cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); + vecCon->gtSimdVal.f64[simdLength - 1 - index] = cnsVal; + } + break; + } + + default: + { + unreached(); + } + } + + return vecCon; + } +#ifdef TARGET_WASM + else + { + // non const Vector128.Create not yet implemented on Wasm + return nullptr; + } +#endif + + IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), sig->numArgs); + + for (int i = sig->numArgs - 1; i >= 0; i--) + { + GenTree* arg = impPopStack().val; + nodeBuilder.AddOperand(i, arg); + } + + return gtNewSimdHWIntrinsicNode(retType, std::move(nodeBuilder), intrinsic, simdBaseType, simdSize); +} + //------------------------------------------------------------------------ // addRangeCheckIfNeeded: add a GT_BOUNDS_CHECK node for non-full-range imm-intrinsic // @@ -2275,6 +2441,8 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, if ((simdSize != 8) && (simdSize != 16) && (simdSize != SIZE_UNKNOWN)) #elif defined(TARGET_XARCH) if ((simdSize != 16) && (simdSize != 32) && (simdSize != 64)) +#elif defined(TARGET_WASM) + if (simdSize != 16) #endif // TARGET_* { assert(!"Unexpected SIMD size"); diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index e993f91b3a3399..27c17c2befd5ab 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -1463,8 +1463,100 @@ struct HWIntrinsic final } } }; +#elif defined(TARGET_WASM) +struct HWIntrinsic final +{ + HWIntrinsic(const GenTreeHWIntrinsic* node) + : op1(nullptr) + , op2(nullptr) + , op3(nullptr) + , numOperands(0) + , baseType(TYP_UNDEF) + { + assert(node != nullptr); -#endif // TARGET_ARM64 + id = node->GetHWIntrinsicId(); + category = HWIntrinsicInfo::lookupCategory(id); + + assert(HWIntrinsicInfo::RequiresCodegen(id)); + + InitializeOperands(node); + InitializeBaseType(node); + } + + bool codeGenIsTableDriven() const + { + bool isTableDrivenCategory = category != HW_Category_Helper; + bool isTableDrivenFlag = !HWIntrinsicInfo::HasSpecialCodegen(id); + + return isTableDrivenCategory && isTableDrivenFlag; + } + + NamedIntrinsic id; + HWIntrinsicCategory category; + GenTree* op1; + GenTree* op2; + GenTree* op3; + size_t numOperands; + var_types baseType; + +private: + void InitializeOperands(const GenTreeHWIntrinsic* node) + { + numOperands = node->GetOperandCount(); + + switch (numOperands) + { + case 3: + op3 = node->Op(3); + FALLTHROUGH; + case 2: + op2 = node->Op(2); + FALLTHROUGH; + case 1: + op1 = node->Op(1); + FALLTHROUGH; + case 0: + break; + + default: + unreached(); + } + } + + void InitializeBaseType(const GenTreeHWIntrinsic* node) + { + baseType = node->GetSimdBaseType(); + + if (baseType == TYP_UNKNOWN) + { + assert((category == HW_Category_Scalar) || (category == HW_Category_Special)); + + if (HWIntrinsicInfo::BaseTypeFromFirstArg(id)) + { + assert(op1 != nullptr); + baseType = op1->TypeGet(); + } + else if (HWIntrinsicInfo::BaseTypeFromSecondArg(id)) + { + // TODO-WASM: This case can likely be dropped + assert(op2 != nullptr); + baseType = op2->TypeGet(); + } + else + { + baseType = node->TypeGet(); + } + + if (category == HW_Category_Scalar) + { + baseType = genActualType(baseType); + } + } + } +}; + +#endif // TARGET_WASM #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 2f8b9988c7810e..e21b4730c7eec9 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -1127,151 +1127,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector64_Create: case NI_Vector128_Create: { - if (sig->numArgs == 1) - { - op1 = impPopStack().val; - retNode = gtNewSimdCreateBroadcastNode(retType, op1, simdBaseType, simdSize); - break; - } - - uint32_t simdLength = getSIMDVectorLength(simdSize, simdBaseType); - assert(sig->numArgs == simdLength); - - bool isConstant = true; - - if (varTypeIsFloating(simdBaseType)) - { - for (uint32_t index = 0; index < sig->numArgs; index++) - { - GenTree* arg = impStackTop(index).val; - - if (!arg->IsCnsFltOrDbl()) - { - isConstant = false; - break; - } - } - } - else - { - assert(varTypeIsIntegral(simdBaseType)); - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - GenTree* arg = impStackTop(index).val; - - if (!arg->IsIntegralConst()) - { - isConstant = false; - break; - } - } - } - - if (isConstant) - { - // Some of the below code assumes 8 or 16 byte SIMD types - assert((simdSize == 8) || (simdSize == 16)); - - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - switch (simdBaseType) - { - case TYP_BYTE: - case TYP_UBYTE: - { - uint8_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u8[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_SHORT: - case TYP_USHORT: - { - uint16_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u16[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_INT: - case TYP_UINT: - { - uint32_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u32[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_LONG: - case TYP_ULONG: - { - uint64_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u64[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_FLOAT: - { - float cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_DOUBLE: - { - double cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f64[simdLength - 1 - index] = cnsVal; - } - break; - } - - default: - { - unreached(); - } - } - - retNode = vecCon; - break; - } - - IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), sig->numArgs); - - for (int i = sig->numArgs - 1; i >= 0; i--) - { - GenTree* arg = impPopStack().val; - nodeBuilder.AddOperand(i, arg); - } - - retNode = gtNewSimdHWIntrinsicNode(retType, std::move(nodeBuilder), intrinsic, simdBaseType, simdSize); + retNode = impSimdCreate(intrinsic, sig, simdBaseType, retType, simdSize); break; } diff --git a/src/coreclr/jit/hwintrinsiccodegenwasm.cpp b/src/coreclr/jit/hwintrinsiccodegenwasm.cpp index 245cf1f38c9c9a..310cb836db3ab3 100644 --- a/src/coreclr/jit/hwintrinsiccodegenwasm.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenwasm.cpp @@ -26,7 +26,32 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX // void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { - NYI_WASM_SIMD("genHWIntrinsic"); + // emitIns_v128_Imm + // emitIns_Lane + // emitIns_Memarg_Lane + + const HWIntrinsic info(node); + genConsumeMultiOpOperands(node); + + if (info.codeGenIsTableDriven()) + { + instruction const ins = HWIntrinsicInfo::lookupIns(info.id, info.baseType, m_compiler); + assert(ins != INS_invalid); + if (info.category == HW_Category_SIMD) + { + GetEmitter()->emitIns(ins); + } + else + { + NYI_WASM_SIMD("!HW_Category_SIMD"); + } + } + else + { + NYI_WASM_SIMD("!codeGenIsTableDriven"); + } + + WasmProduceReg(node); } #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/hwintrinsiclistwasm.h b/src/coreclr/jit/hwintrinsiclistwasm.h index 872766bc39f685..692835d03476fc 100644 --- a/src/coreclr/jit/hwintrinsiclistwasm.h +++ b/src/coreclr/jit/hwintrinsiclistwasm.h @@ -32,19 +32,19 @@ HARDWARE_INTRINSIC(Vector128, AsUInt32, HARDWARE_INTRINSIC(Vector128, AsUInt64, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AsVector128Unsafe, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AsVector2, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, AsVector3, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, AsVector3, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, AsVector4, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, Create, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, Create, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, CreateScalar, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, CreateScalarUnsafe, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Dot, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ExtractMostSignificantBits, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, GetElement, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, GetElement, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, Shuffle, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ShuffleNative, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, ShuffleNativeFallback, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, ToScalar, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_InvalidNodeId) -HARDWARE_INTRINSIC(Vector128, WithElement, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) +HARDWARE_INTRINSIC(Vector128, ToScalar, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_InvalidNodeId|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ReturnsScalarT) +HARDWARE_INTRINSIC(Vector128, WithElement, 16, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, WithLower, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, WithUpper, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) HARDWARE_INTRINSIC(Vector128, get_AllBitsSet, 16, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) @@ -64,6 +64,74 @@ HARDWARE_INTRINSIC(Vector128, op_Inequality, HARDWARE_INTRINSIC(Vector128, op_UnaryPlus, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_InvalidNodeId) #define LAST_NI_Vector128 NI_Vector128_op_UnaryPlus + +#define FIRST_NI_PackedSimd NI_PackedSimd_Abs +HARDWARE_INTRINSIC(PackedSimd, Abs, 16, 1, {INS_i8x16_abs, INS_invalid, INS_i16x8_abs, INS_invalid, INS_i32x4_abs, INS_invalid, INS_i64x2_abs, INS_invalid, INS_f32x4_abs, INS_f64x2_abs }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Add, 16, 2, {INS_i8x16_add, INS_i8x16_add, INS_i16x8_add, INS_i16x8_add, INS_i32x4_add, INS_i32x4_add, INS_i64x2_add, INS_i64x2_add, INS_f32x4_add, INS_f64x2_add }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, AddPairwiseWidening, 16, 1, {INS_i16x8_extadd_pairwise_s_i8x16, INS_i16x8_extadd_pairwise_u_i8x16, INS_i32x4_extadd_pairwise_s_i16x8, INS_i32x4_extadd_pairwise_u_i16x8, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, AddSaturate, 16, 2, {INS_i8x16_add_sat_s, INS_i8x16_add_sat_u, INS_i16x8_add_sat_s, INS_i16x8_add_sat_u, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, AllTrue, 16, 1, {INS_i8x16_all_true, INS_i8x16_all_true, INS_i16x8_all_true, INS_i16x8_all_true, INS_i32x4_all_true, INS_i32x4_all_true, INS_i64x2_all_true, INS_i64x2_all_true, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, And, 16, 2, {INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and, INS_v128_and }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, AndNot, 16, 2, {INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot, INS_v128_andnot }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, AnyTrue, 16, 1, {INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true, INS_v128_any_true }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_ReturnsBoolean) +HARDWARE_INTRINSIC(PackedSimd, AverageRounded, 16, 2, {INS_invalid, INS_i8x16_avgr_u, INS_invalid, INS_i16x8_avgr_u, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, Bitmask, 16, 1, {INS_i8x16_bitmask, INS_i8x16_bitmask, INS_i16x8_bitmask, INS_i16x8_bitmask, INS_i32x4_bitmask, INS_i32x4_bitmask, INS_i64x2_bitmask, INS_i64x2_bitmask, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +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 }, 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 }, 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 }, 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 }, 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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) + +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 }, 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 }, 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 }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, ConvertToDoubleLower, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f64x2_convert_low_s_i32x4, INS_f64x2_convert_low_u_i32x4, INS_invalid, INS_invalid, INS_f64x2_promote_low_f32x4, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, ConvertToInt32Saturate, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_i32x4_trunc_sat_s_f32x4, INS_i32x4_trunc_sat_s_f64x2_zero}, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, ConvertToSingle, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_convert_s_i32x4, INS_f32x4_convert_u_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_demote_f64x2_zero }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +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}, 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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, MultiplyRoundedSaturateQ15, 16, 2, {INS_invalid, INS_invalid, INS_i16x8_q15mulr_sat_s, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, MultiplyWideningLower, 16, 2, {INS_i16x8_extmul_low_s_i8x16, INS_i16x8_extmul_low_u_i8x16, INS_i32x4_extmul_low_s_i16x8, INS_i32x4_extmul_low_u_i16x8, INS_i64x2_extmul_low_s_i32x4, INS_i64x2_extmul_low_u_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, MultiplyWideningUpper, 16, 2, {INS_i16x8_extmul_high_s_i8x16, INS_i16x8_extmul_high_u_i8x16, INS_i32x4_extmul_high_s_i16x8, INS_i32x4_extmul_high_u_i16x8, INS_i64x2_extmul_high_s_i32x4, INS_i64x2_extmul_high_u_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Negate, 16, 1, {INS_i8x16_neg, INS_i8x16_neg, INS_i16x8_neg, INS_i16x8_neg, INS_i32x4_neg, INS_i32x4_neg, INS_i64x2_neg, INS_i64x2_neg, INS_f32x4_neg, INS_f64x2_neg }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Not, 16, 1, {INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not, INS_v128_not }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Or, 16, 2, {INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or, INS_v128_or }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +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 }, 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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, HW_Category_Helper, HW_Flag_InvalidNodeId) +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 }, 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 }, 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 }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Truncate, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_f32x4_trunc, INS_f64x2_trunc }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, Xor, 16, 2, {INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor, INS_v128_xor }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_Commutative) +HARDWARE_INTRINSIC(PackedSimd, ZeroExtendWideningLower, 16, 1, {INS_i16x8_extend_low_u_i8x16, INS_i16x8_extend_low_u_i8x16, INS_i32x4_extend_low_u_i16x8, INS_i32x4_extend_low_u_i16x8, INS_i64x2_extend_low_u_i32x4, INS_i64x2_extend_low_u_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(PackedSimd, ZeroExtendWideningUpper, 16, 1, {INS_i16x8_extend_high_u_i8x16, INS_i16x8_extend_high_u_i8x16, INS_i32x4_extend_high_u_i16x8, INS_i32x4_extend_high_u_i16x8, INS_i64x2_extend_high_u_i32x4, INS_i64x2_extend_high_u_i32x4, INS_invalid, INS_invalid, INS_invalid, INS_invalid }, HW_Category_SIMD, HW_Flag_BaseTypeFromFirstArg) +#define LAST_NI_PackedSimd NI_PackedSimd_ZeroExtendWideningUpper #endif // FEATURE_HW_INTRINSICS #undef HARDWARE_INTRINSIC diff --git a/src/coreclr/jit/hwintrinsicwasm.cpp b/src/coreclr/jit/hwintrinsicwasm.cpp index bd6c38fdb9d984..2f25e4506e12ce 100644 --- a/src/coreclr/jit/hwintrinsicwasm.cpp +++ b/src/coreclr/jit/hwintrinsicwasm.cpp @@ -76,8 +76,97 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, unsigned simdSize, bool mustExpand) { - NYI_WASM_SIMD("impSpecialIntrinsic"); - return nullptr; + assert(varTypeIsArithmetic(simdBaseType)); + + GenTree* retNode = nullptr; + GenTree* op1 = nullptr; + GenTree* op2 = nullptr; + + switch (intrinsic) + { + case NI_Vector128_GetElement: + case NI_Vector128_WithElement: + { + // Vector128.GetElement / WithElement have valid managed implementations in + // Vector128.cs. Return nullptr to let the importer fall back to those rather + // than asserting / NYI'ing in the JIT. + return nullptr; + } + + // 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: + case NI_PackedSimd_CompareGreaterThanOrEqual: + case NI_PackedSimd_CompareLessThan: + case NI_PackedSimd_CompareLessThanOrEqual: + break; + + 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); + } + + case NI_PackedSimd_LoadScalarVector128: + case NI_PackedSimd_LoadScalarAndSplatVector128: + case NI_PackedSimd_LoadScalarAndInsert: + case NI_PackedSimd_LoadWideningVector128: + break; + + case NI_PackedSimd_Store: + { + assert(sig->numArgs == 2); + assert(simdSize == 16); + op2 = impPopStack().val; + 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 gtNewSimdStoreNode(op1, op2, simdBaseType, simdSize); + } + + case NI_PackedSimd_StoreSelectedScalar: + break; + + case NI_PackedSimd_ShiftLeft: + case NI_PackedSimd_ShiftRightArithmetic: + case NI_PackedSimd_ShiftRightLogical: + break; + + case NI_PackedSimd_Splat: + break; + + case NI_Vector128_Create: + { + retNode = impSimdCreate(intrinsic, sig, simdBaseType, retType, simdSize); + break; + } + + default: + { + break; + } + } + + return retNode; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index d4972a9fb3d5b7..784b1d7d6a1c6c 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -2031,148 +2031,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector256_Create: case NI_Vector512_Create: { - if (sig->numArgs == 1) - { - op1 = impPopStack().val; - retNode = gtNewSimdCreateBroadcastNode(retType, op1, simdBaseType, simdSize); - break; - } - - uint32_t simdLength = getSIMDVectorLength(simdSize, simdBaseType); - assert(sig->numArgs == simdLength); - - bool isConstant = true; - - if (varTypeIsFloating(simdBaseType)) - { - for (uint32_t index = 0; index < sig->numArgs; index++) - { - GenTree* arg = impStackTop(index).val; - - if (!arg->IsCnsFltOrDbl()) - { - isConstant = false; - break; - } - } - } - else - { - assert(varTypeIsIntegral(simdBaseType)); - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - GenTree* arg = impStackTop(index).val; - - if (!arg->IsIntegralConst()) - { - isConstant = false; - break; - } - } - } - - if (isConstant) - { - GenTreeVecCon* vecCon = gtNewVconNode(retType); - - switch (simdBaseType) - { - case TYP_BYTE: - case TYP_UBYTE: - { - uint8_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u8[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_SHORT: - case TYP_USHORT: - { - uint16_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u16[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_INT: - case TYP_UINT: - { - uint32_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u32[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_LONG: - case TYP_ULONG: - { - uint64_t cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsIntConCommon()->IntegralValue()); - vecCon->gtSimdVal.u64[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_FLOAT: - { - float cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f32[simdLength - 1 - index] = cnsVal; - } - break; - } - - case TYP_DOUBLE: - { - double cnsVal = 0; - - for (uint32_t index = 0; index < sig->numArgs; index++) - { - double cnsVal = static_cast(impPopStack().val->AsDblCon()->DconValue()); - vecCon->gtSimdVal.f64[simdLength - 1 - index] = cnsVal; - } - break; - } - - default: - { - unreached(); - } - } - - retNode = vecCon; - break; - } - - IntrinsicNodeBuilder nodeBuilder(getAllocator(CMK_ASTNode), sig->numArgs); - - for (int i = sig->numArgs - 1; i >= 0; i--) - { - GenTree* arg = impPopStack().val; - nodeBuilder.AddOperand(i, arg); - } - - retNode = gtNewSimdHWIntrinsicNode(retType, std::move(nodeBuilder), intrinsic, simdBaseType, simdSize); + retNode = impSimdCreate(intrinsic, sig, simdBaseType, retType, simdSize); break; } diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 500e94832339cc..e33d810f40fd70 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -2116,6 +2116,10 @@ instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false* return INS_f32_load; case TYP_DOUBLE: return INS_f64_load; +#if defined(FEATURE_SIMD) + case TYP_SIMD16: + return INS_v128_load; +#endif default: NYI_WASM("ins_Load"); return INS_none; @@ -2525,6 +2529,10 @@ instruction CodeGenInterface::ins_Store(var_types dstType, bool aligned /*=false return INS_f32_store; case TYP_DOUBLE: return INS_f64_store; +#if defined(FEATURE_SIMD) + case TYP_SIMD16: + return INS_v128_store; +#endif default: NYI_WASM("ins_Store"); return INS_none; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index b55ca2619dee3b..0c04a216fb9c99 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -9477,7 +9477,11 @@ void Lowering::CheckNode(Compiler* compiler, GenTree* node) #ifdef FEATURE_SIMD case GT_HWINTRINSIC: + // TODO-WASM: SIMD12 is still maintained through lowering. + // Fix the below once we've determined our lowering path for SIMD12. +#ifndef TARGET_WASM assert(!node->TypeIs(TYP_SIMD12)); +#endif break; #endif // FEATURE_SIMD diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 78cda8482f64d6..b72d6ffc3fd7dd 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -820,8 +820,17 @@ void Lowering::AfterLowerArgsForCall(GenTreeCall* call) // GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) { - NYI_WASM_SIMD("Lowering::LowerHWIntrinsic"); - return node; + HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(node->GetHWIntrinsicId()); + switch (category) + { + case HWIntrinsicCategory::HW_Category_SIMD: + { + break; + } + default: + NYI_WASM_SIMD("Lowering::LowerHWIntrinsic"); + } + return node->gtNext; } //---------------------------------------------------------------------------------------------- @@ -832,5 +841,5 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) // void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) { - NYI_WASM_SIMD("Lowering::ContainCheckHWIntrinsic"); + // TODO-WASM: implement containment for hardware intrinsics, currently a no-op } diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 47aadbd45f8e30..dc85979d1f3add 100644 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -2405,6 +2405,21 @@ def create_exception(): # No miss/replay failure is expected in contexts that were reported as having diffs since then they succeeded during the diffs run. raise create_exception() + # A Wasm JIT may exit successfully without writing any disassembly to DOTNET_JitStdOutFile. For example, the wasm JIT + # with JitWasmSimdNyiToR2RUnsupported=1 exits via + # implReadyToRunUnsupported() (CORJIT_R2R_UNSUPPORTED) for NYI_WASM_SIMD during import, so no code is produced. This is an expected behavior. + # TODO-WASM: This check can potentially be removed once we no longer have any NYI's in the import stage. + if not os.path.exists(item_path) and self.coreclr_args.target_arch == "wasm": + # Log a warning so that unexpected misses (vs the expected JitWasmSimdNyiToR2RUnsupported path) remain diagnosable + # rather than being silently masked as empty diffs. + stderr_snippet = stderr.decode(errors='replace').strip().splitlines() + stderr_first_line = stderr_snippet[0] if stderr_snippet else "" + logging.warning( + "%sNo JitStdOutFile produced for wasm context %s at %s (exit=%d, stderr first line: %r). " + "Treating as empty diff; verify this is the expected JitWasmSimdNyiToR2RUnsupported path.", + print_prefix, context_index, item_path, proc.returncode, stderr_first_line) + return "" + try: with open(item_path, 'r') as file_handle: return file_handle.read() diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index b1ff24e8b804f7..106763c0828323 100644 --- a/src/coreclr/tools/Common/InstructionSetHelpers.cs +++ b/src/coreclr/tools/Common/InstructionSetHelpers.cs @@ -94,6 +94,11 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru } } } + else if (targetArchitecture == TargetArchitecture.Wasm32) + { + instructionSetSupportBuilder.AddSupportedInstructionSet("base"); + instructionSetSupportBuilder.AddSupportedInstructionSet("simd128"); + } bool throttleAvx512 = false; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 94bc31d6a10a45..58c42543bccab8 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -16,6 +16,7 @@ true true true + true false true false