[Wasm RyuJIT] Codegen for Const Vector Create and Basic Packed SIMD operations#129703
[Wasm RyuJIT] Codegen for Const Vector Create and Basic Packed SIMD operations#129703adamperlin wants to merge 23 commits into
Conversation
Enable Wasm SIMD ISA for use in the JIT
…simd, simd) param type operations (no integer operands or immediates supported yet)
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR extends CoreCLR’s Wasm (RyuJIT) pipeline to recognize Wasm SIMD instruction-set support and begin emitting v128 SIMD code, including constant Vector128.Create lowering to v128.const and table-driven codegen for many PackedSimd operations.
Changes:
- Enable Wasm ISA flags (
WasmBase,PackedSimd,Vector128) and wire up instruction-set support for Wasm32 (base,simd128). - Add
GT_CNS_VECemission viav128.const, plus v128 load/store selection forTYP_SIMD16. - Introduce initial importer/list/codegen plumbing for
PackedSimdandVector128.Createconstants.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems | Enables SupportsWasmIntrinsics for CoreCLR Wasm builds. |
| src/coreclr/tools/Common/InstructionSetHelpers.cs | Adds Wasm32 baseline instruction sets (base, simd128). |
| src/coreclr/jit/lowerwasm.cpp | Starts allowing SIMD-category GT_HWINTRINSIC through lowering. |
| src/coreclr/jit/lower.cpp | Relaxes TYP_SIMD12 assert for Wasm during lowering checks. |
| src/coreclr/jit/instr.cpp | Adds Wasm v128_load/v128_store mapping for TYP_SIMD16. |
| src/coreclr/jit/hwintrinsicwasm.cpp | Implements constant Vector128.Create import to GT_CNS_VEC. |
| src/coreclr/jit/hwintrinsiclistwasm.h | Adds PackedSimd intrinsic definitions and updates some Vector128 entries. |
| src/coreclr/jit/hwintrinsiccodegenwasm.cpp | Adds initial table-driven Wasm HW intrinsic emission. |
| src/coreclr/jit/hwintrinsic.h | Adds a Wasm HWIntrinsic helper wrapper (but currently has a preprocessor issue). |
| src/coreclr/jit/hwintrinsic.cpp | Adds Wasm PackedSimd ISA range and simdSize validation for Wasm. |
| src/coreclr/jit/compiler.cpp | Forces Wasm baseline ISAs into JIT supported set, adds Vector128. |
| src/coreclr/jit/codegenwasm.cpp | Adds GT_HWINTRINSIC dispatch and GT_CNS_VEC emission (v128.const). |
| src/coreclr/jit/codegen.h | Declares Wasm vector-constant codegen helper. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…case in SPMI (can happen if NYI_WASM_SIMD fires during import)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| 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, 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) |
| # 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": | ||
| return "" |
…ck to managed implementation, and implement HARDWARE_INTRINSIC() stubs for all packed simd opcodes to ensure NYI
…ntime into adamperlin/wasm-basic-simd
|
@AndyAyersMS Copilot feedback has been implemented for the most part, so this one should be ready for another look. Now all of the PackedSimd intrinsics are at least stubbed out in the table and should cause NYI, so we won't end up trying to call the infinitely recursive managed versions. |
|
@tannergooding Could you please take a look at this when you have a chance? I don't think a fallback to managed path is possible for the PackedSimd intrinsics as they're just defined as self-calling. |
Looking.
This is expected. It's only the xplat APIs (i.e. For the platform specified APIs (i.e. For cases where the ISA is supported, but is not fully lit up, there is then a fallback to |
| else if (targetArchitecture == TargetArchitecture.Wasm32) | ||
| { | ||
| instructionSetSupportBuilder.AddSupportedInstructionSet("base"); | ||
| instructionSetSupportBuilder.AddSupportedInstructionSet("simd128"); |
There was a problem hiding this comment.
Why simd128 and not packedsimd or similar?
| NYI_WASM_SIMD("PackedSimd.ReplaceScalar"); | ||
| break; | ||
|
|
||
| case NI_PackedSimd_LoadVector128: |
There was a problem hiding this comment.
This one should just be gtNewSimdLoadNode, it only produces a GT_IND so the regular addressing mode and other support can kick in, similar with Store.
It's only the scalar and special semantic instructions which can't be represented as a "regular indirection" that we want to persist as GT_HWINTRINSIC nodes here.
| NYI_WASM_SIMD("PackedSimd.Splat"); | ||
| break; | ||
|
|
||
| case NI_Vector128_Create: |
There was a problem hiding this comment.
We have nearly this same logic duplicated across xarch, arm64, and wasm now. It'd probably be good to have some impSimdCreate(intrinsicId) or similar so that the whole "pop stack and check if everything is constant" logic can be shared across them.
Co-authored-by: Tanner Gooding <tagoo@microsoft.com>
This PR implements support for
Vector128.Createon Wasm with known constant args, as well as table-driven expansion and codegen for a large set of PackedSimd opcodes that operate on v128, (v128, v128) and (v128, v128, v128) type operands.Notably absent from this PR: Vector128.Create with non-constants (requires additional lowering work), and SIMD compares (need special codegen for ulong since i64_u compare is not natively supported on Wasm).