JIT: eliminate redundant span checks across loop back-edge phis#127117
JIT: eliminate redundant span checks across loop back-edge phis#127117
Conversation
Recognize that a back-edge phi-arg's VN equals the (checkedBound + cns) form referenced by an O2K_CHECKED_BOUND_ADD_CNS assertion in RangeCheck::MergeEdgeAssertions. Read phi-arg VNs from the SSA def's m_vnPair in optVisitReachingAssertions; this is always at least as precise as phiArg->gtVNPair (which fgValueNumberPhiDef may leave as NoVN or stale when it refuses to back-patch a loop-varying VN). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Optimize CoreCLR JIT range/argument check elimination for spans in loops by improving how edge assertions are interpreted across loop back-edge phi arguments.
Changes:
- Extend
RangeCheck::MergeEdgeAssertionsto recognize when the tracked VN matches a checked-bound expression viaADD(boundVN, cns), enabling assertion application to loop back-edge phi-args. - Update
Compiler::optVisitReachingAssertionsto read the VN for a phi-arg from its SSA def’sm_vnPair(instead of the potentially stalegtVNPair), improving precision for assertion propagation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/coreclr/jit/rangecheck.cpp |
Enhances checked-bound assertion matching so loop back-edge phi args can inherit assertions and eliminate redundant checks. |
src/coreclr/jit/compiler.hpp |
Uses SSA-def VN for phi args during reaching-assertion visitation to avoid gtVNPair staleness/NoVN cases. |
- optVisitReachingAssertions: only fall back to the SSA def's m_vnPair when phiArg->gtVNPair is NoVN, restoring the cheap fast-path for the common case. - MergeEdgeAssertions: skip the IsVNBinFuncWithConst lookup when boundCns == 0, since VN normalizes ADD(x, 0) to x, so any positive match would already be caught by the trivial check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e5170d0 to
ff00e93
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
PTAL @jakobbotsch @dotnet/jit-contrib |
| auto visitor = [comp, &phiRange, &budget](ValueNum reachingVN, ASSERT_TP reachingAssertions) { | ||
| // call GetRangeFromAssertions for each reaching VN using reachingAssertions | ||
| Range edgeRange = GetRangeFromAssertions(comp, reachingVN, reachingAssertions, --budget); | ||
| Range edgeRange = GetRangeFromAssertions(comp, reachingVN, reachingAssertions, min(3, --budget)); |
There was a problem hiding this comment.
After I switched optVisitReachingAssertion to SSA defs instead of VNs it significantly regressed TP, this helped to reduce it 3x times
| // fgValueNumberPhiDef may leave gtVNPair as NoVN when it refuses to back-patch | ||
| // a loop-varying SSA-def VN into the phi-arg slot (a hygiene constraint for general | ||
| // gtVNPair consumers that does not apply here, since we hand the VN to the visitor | ||
| // together with this edge's assertion set and never write it back into any tree). | ||
| // Use the SSA def's VN directly; by SSA construction it names the same value. | ||
| LclSsaVarDsc* phiArgSsaDef = lvaGetDesc(phiArg)->GetPerSsaData(phiArg->GetSsaNum()); | ||
| const ValueNum phiArgVN = vnStore->VNConservativeNormalValue(phiArgSsaDef->m_vnPair); |
There was a problem hiding this comment.
Not clear to me if this is legal... See #106229 and the issue it links to. The VNs from the phi def can be from a different loop iteration and it can take on a different runtime value.
There was a problem hiding this comment.
@jakobbotsch but isn't similar how rangecheck works? where we iterate phi via SSA and call MergeEdgeAssertions on them?

(we obtain VN out of the tree inside)
Contributes to #111309
Optimize redundant range/argument checks for spans inside loops.
Codegen diff
G_M38854_IG02: mov rax, bword ptr [rcx] mov ecx, dword ptr [rcx+0x08] vxorps xmm0, xmm0, xmm0 cmp ecx, 4 jl SHORT G_M38854_IG04 G_M38854_IG03: - cmp ecx, 4 - jl SHORT G_M38854_IG09 vpaddd xmm0, xmm0, xmmword ptr [rax] add rax, 16 add ecx, -4 cmp ecx, 4 jge SHORT G_M38854_IG03 G_M38854_IG04: vpsrldq xmm1, xmm0, 8 vpaddd xmm0, xmm1, xmm0 vpsrldq xmm1, xmm0, 4 vpaddd xmm0, xmm1, xmm0 vmovd edx, xmm0 test ecx, ecx je SHORT G_M38854_IG07 G_M38854_IG05: xor r8d, r8d G_M38854_IG06: add edx, dword ptr [rax+r8] add r8, 4 dec ecx jne SHORT G_M38854_IG06 G_M38854_IG07: mov eax, edx G_M38854_IG08: - add rsp, 40 ret -G_M38854_IG09: - mov ecx, 6 - call [System.ThrowHelper:ThrowArgumentOutOfRangeException(int)] - int3 -; Total bytes of code 113 +; Total bytes of code 79Diffs