Skip to content

[RyuJIT Wasm] SIMD Codegen for ExtractScalar/ReplaceScalar, Unsigned Compares#130272

Open
adamperlin wants to merge 22 commits into
dotnet:mainfrom
adamperlin:adamperlin/wasm-simd-unsigned-compare-lane-ops
Open

[RyuJIT Wasm] SIMD Codegen for ExtractScalar/ReplaceScalar, Unsigned Compares#130272
adamperlin wants to merge 22 commits into
dotnet:mainfrom
adamperlin:adamperlin/wasm-simd-unsigned-compare-lane-ops

Conversation

@adamperlin

@adamperlin adamperlin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

This PR implements Wasm codegen for ExtractScalar and ReplaceScalar, as well as enabling codegen for SIMD shifts. As an additional change, lowering for ulong type compares is implemented with a rewrite to an equivalent signed compare.

ExtractScalar and ReplaceScalar require immediate operands, so we need to generate some kind of jump-table fallback for each possible immediate value in cases where we can't guarantee that the operand is an immediate. The approach I've taken here is to generate the table as a series of inline nested wasm blocks. This also means we need to mark the non-immediate operands as MultiplyUsed and re-materialize the computed values on the wasm value stack in each case in the jump table (since nested blocks can't reach "up" the value stack, unless there are explicit block parameters).

As an additional change, we treat SIMD types like structs for ABI handling purposes (before they were considered pass-by-value types which conflicted with ABI classification elsewhere). We aren't planning to introduce by-value v128 in the ABI for .NET 11.

Copilot AI review requested due to automatic review settings July 7, 2026 00:36
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the WASM RyuJIT SIMD pipeline by enabling table-driven codegen for additional PackedSimd intrinsics (notably ExtractScalar/ReplaceScalar), adding lowering support for Vector128<ulong> ordered compares via a signed-compare rewrite, and introducing a non-constant-immediate fallback path using a structured-control-flow “jump table” emission pattern.

Changes:

  • Add lowering rewrite for Vector128<ulong> ordered compares by XOR’ing the sign bit and switching to signed compares.
  • Implement immediate-operand handling for WASM SIMD: containment for constant immediates and a jump-table fallback for non-constant immediates (plus RA support).
  • Enable/route additional WASM SIMD intrinsics through the table-driven pipeline and update lane-emission plumbing.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/coreclr/jit/targetwasm.cpp Adjusts ABI classification to treat TYP_SIMD16 similarly to structs for WASM ABI lowering decisions.
src/coreclr/jit/regallocwasm.h Adds a regalloc hook to handle HWIntrinsic nodes needing special multi-use handling.
src/coreclr/jit/regallocwasm.cpp Consumes temp regs for HWIntrinsic operands when jump-table fallback is required.
src/coreclr/jit/lowerwasm.cpp Adds immediate handling for HW intrinsics and a rewrite for unsigned i64x2 ordered compares.
src/coreclr/jit/lower.h Declares a WASM-specific lowering helper for unsigned-long SIMD compares.
src/coreclr/jit/hwintrinsicwasm.cpp Implements WASM immediate upper-bound computation and improves immediate operand discovery.
src/coreclr/jit/hwintrinsiclistwasm.h Updates intrinsic table entries to enable more PackedSimd ops (ordered compares, extract/replace lane, shifts, splat).
src/coreclr/jit/hwintrinsiccodegenwasm.cpp Adds codegen support for HW_Category_IMM, including a jump-table fallback for non-constant immediates.
src/coreclr/jit/hwintrinsic.h Extends WASM HWIntrinsic info with immediate-position helpers and jump-table-fallback querying.
src/coreclr/jit/hwintrinsic.cpp Updates the WASM callsite to lookupImmUpperBound with the new signature.
src/coreclr/jit/gentree.h Introduces a WASM-only HWIntrinsic flag for “needs jump table fallback”.
src/coreclr/jit/emitwasm.h Updates the emitIns_Lane signature to derive element size from the instruction.
src/coreclr/jit/emitwasm.cpp Implements the new emitIns_Lane behavior and makes jump debug-info tolerant of null targets.
src/coreclr/jit/compiler.h Makes Compiler::getSIMDVectorLength(simdSize, baseType) publicly callable for WASM intrinsic helpers.
src/coreclr/jit/codegenwasm.cpp Adjusts range-check throw kind usage; adds helper block emit APIs; updates SIMD emitter unit tests; tweaks SIMD16 call-arg guard.
src/coreclr/jit/codegen.h Declares new WASM helper APIs for emitting raw block/end pairs and HWIntrinsic jump-table fallback.

Comment thread src/coreclr/jit/hwintrinsiclistwasm.h Outdated
Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp
Comment thread src/coreclr/jit/hwintrinsic.h Outdated
Comment thread src/coreclr/jit/emitwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp
Comment thread src/coreclr/jit/emitwasm.cpp
Comment thread src/coreclr/jit/gentree.h
GTF_HW_EM_OP = 0x10000000, // GT_HWINTRINSIC -- node is used as an operand to an embedded mask
GTF_HW_USER_CALL = 0x20000000, // GT_HWINTRINSIC -- node is implemented via a user call
#ifdef TARGET_WASM
GTF_HW_NEEDS_JUMP_TABLE = 0x40000000, // GT_HWINTRINSIC -- (WASM) intrinsic has a non-constant immediate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really a small optimization to avoid having to re-check the operands for a GT_HWINTRINSIC over and over again to see if a jump table is needed (this mostly comes up in regalloc, where we only need to mark the operands for an intrinsic as MultiplyUsed if it needs a jump table)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it expensive to do for wasm?

On most platforms this is just checking if (lastOp->IsCnsIntOrI()) and we already have the last op, so it avoids needing to burn any flag.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The immediate value isn't always lastOp on Wasm, so I think we'd have to use HWIntrinsicInfo::GetImmOpsPositions or cache that information, I think.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to pull all the operands anyways, so is that necessarily a bad thing?

The main concern I have is that the available GenTree flags are scarce. The need for a jump table is then expected to be rare and such nodes are rare enough that it's unlikely to even show up on the SPMI throughput tracking, so just doing this the "same way" as all other architectures (i.e. checking immOp->IsCnsIntOrI()) feels "best"

Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp
Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 7, 2026 17:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Comment thread src/coreclr/jit/targetwasm.cpp Outdated
Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp
Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp Outdated
Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
Copilot AI review requested due to automatic review settings July 7, 2026 19:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/coreclr/jit/hwintrinsic.cpp:2329

  • Non-constant lane immediates for Wasm HW_Category_IMM intrinsics still won't get an ArgumentOutOfRangeException range-check: addRangeCheckIfNeeded is only applied in the numArgs == 2 (op2) and numArgs == 4 (op4) cases, but PackedSimd.ReplaceScalar, PackedSimd.LoadScalarAndInsert, and PackedSimd.StoreSelectedScalar are 3-arg intrinsics. With the new jump-table fallback, an out-of-range lane can still fall into the br_table default and trap (unreachable) instead of throwing SCK_ARG_RNG_EXCPN.

To match the existing immediate-operand contract, the importer should apply addRangeCheckIfNeeded to whichever operand is the immediate for numArgs == 3 (using immOp1 discovered by getHWIntrinsicImmOps / GetImmOpsPositions, or by matching the popped operand pointer against immOp1).

        immUpperBound   = HWIntrinsicInfo::lookupImmUpperBound(intrinsic);
        hasFullRangeImm = HWIntrinsicInfo::HasFullRangeImm(intrinsic);
#elif defined(TARGET_WASM)
        immUpperBound = HWIntrinsicInfo::lookupImmUpperBound(intrinsic, simdSize, simdBaseType);
#endif

        if (!CheckHWIntrinsicImmRange(intrinsic, simdBaseType, immOp1, mustExpand, immLowerBound, immUpperBound,
                                      hasFullRangeImm, &useFallback))

Comment thread src/coreclr/jit/lowerwasm.cpp Outdated
@adamperlin adamperlin marked this pull request as ready for review July 9, 2026 20:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp
Comment thread src/coreclr/jit/targetwasm.cpp Outdated
Comment thread src/coreclr/jit/lowerwasm.cpp

@AndyAyersMS AndyAyersMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits on top of what copilot saw.

Comment thread src/coreclr/jit/codegenwasm.cpp
Comment thread src/coreclr/jit/hwintrinsic.h Outdated
}
#endif // TARGET_ARM64
#elif defined(TARGET_WASM)
static void CheckImmOpSignature(NamedIntrinsic id, CORINFO_SIG_INFO* sig)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: missing header comments (here and elsewhere)

Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp
Copilot AI review requested due to automatic review settings July 9, 2026 20:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp
Comment thread src/coreclr/jit/codegen.h
Comment thread src/coreclr/jit/codegenwasm.cpp Outdated
Comment thread src/coreclr/jit/hwintrinsic.h Outdated
Comment thread src/coreclr/jit/hwintrinsic.h Outdated
Comment thread src/coreclr/jit/hwintrinsiccodegenwasm.cpp
Comment thread src/coreclr/jit/hwintrinsiclistwasm.h
…ub.com:adamperlin/runtime into adamperlin/wasm-simd-unsigned-compare-lane-ops
Copilot AI review requested due to automatic review settings July 9, 2026 23:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment on lines +145 to +151
// emit `immUpperBound+1` blocks. The iteration order of the loop doesn't matter here,
// we just need to stage the right number of blocks on the control flow stack.
for (int i = 0; i <= immUpperBound; i++)
{
// emit a block for each case
genEmitBeginBlock();
}
Comment thread src/coreclr/jit/targetwasm.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment on lines +145 to +151
// emit `immUpperBound+1` blocks. The iteration order of the loop doesn't matter here,
// we just need to stage the right number of blocks on the control flow stack.
for (int i = 0; i <= immUpperBound; i++)
{
// emit a block for each case
genEmitBeginBlock();
}
Comment on lines 2750 to +2755
if (type == TYP_SIMD16)
{
// Storing a SIMD16 value emits v128.store, but the data operand is not
// materialized as a v128 (it comes through as an i32), producing an invalid
// module. Bail until SIMD16 store is properly supported.
NYI_WASM_SIMD("SIMD16 store indirect");
// NYI_WASM_SIMD("SIMD16 store indirect");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be commented out?

Comment on lines +869 to +877
case NI_PackedSimd_CompareLessThan:
case NI_PackedSimd_CompareLessThanOrEqual:
case NI_PackedSimd_CompareGreaterThan:
case NI_PackedSimd_CompareGreaterThanOrEqual:
{
if (node->GetSimdBaseType() == TYP_ULONG)
{
return LowerHWIntrinsicCompareUnsignedLong(node);
}
uint8_t elemSize = CodeGenInterface::instSimdElemSize(ins);

// Add element width as an emit attribute
emitAttr attr = EA_UNKNOWN;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this not just be EA_ATTR(size) or similar?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants