JIT: Avoid resolving def-use conflicts by changing use registers#125333
JIT: Avoid resolving def-use conflicts by changing use registers#125333jakobbotsch wants to merge 11 commits intodotnet:mainfrom
Conversation
The only benefit is that it saves insertion of some explicit copies, but the trade off is an implicit contract between LSRA and codegen that is not very faithful. When LSRA defines in a different register than the constrained register, it believes that codegen can insert a copy and simultaneously use it from the new location, which is not true.
There was a problem hiding this comment.
Pull request overview
This PR refactors LinearScan::resolveConflictingDefAndUse in the JIT's register allocator (LSRA) to eliminate the practice of changing use-side register assignments when resolving def-use conflicts. The change ensures that only the definition RefPosition's register assignment is ever modified, while explicit copies are inserted for cases where the use constraint cannot be satisfied. This removes a fragile implicit contract between LSRA and codegen where LSRA would assign a def to a different register than constrained and expect codegen to simultaneously insert a copy and use from the new location.
Changes:
- Simplifies
resolveConflictingDefAndUseinlsrabuild.cppby removing old Cases 1 and 4 (which changed the USE register assignment), consolidating the remaining cases into four cleaner ones that only modify the DEF's register assignment. - Updates the
LsraDumpEventenum inlsra.hto replace numbered case events (CASE1–CASE6) with descriptive names. - Updates the debug dump handler in
lsra.cppto use the new event names and removes the now-deadLSRA_EVENT_DEFUSE_FIXED_DELAY_USEno-op case.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/coreclr/jit/lsrabuild.cpp |
Core logic change: removes use-register-changing cases, simplifies conflict resolution to only modify def registers, updates comments |
src/coreclr/jit/lsra.h |
Replaces numbered DEFUSE case enum values with descriptive names |
src/coreclr/jit/lsra.cpp |
Updates debug dump switch/case to use new enum names, removes dead LSRA_EVENT_DEFUSE_FIXED_DELAY_USE case |
|
Another related issue here is #10196 |
|
/azp run runtime-coreclr jitstressregs |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run runtime-coreclr jitstressregs |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run runtime-coreclr jitstressregs |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
jitstressregs failures looks like the same that are present in main. Rest looks like infra failures, will rerun CI. |
|
/azp run runtime-coreclr jitstressregs, runtime-coreclr jitstress2-jitstressregs |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/azp run runtime-coreclr jitstressregs |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
cc @dotnet/jit-contrib PTAL @AndyAyersMS Diffs. Some minor regressions mostly. I analyzed the arm64 ones and they happen "by chance": EH write-thru variables that cross calls are not strongly preferred into callee saves (unlike non EH-write thru variables). |
| // for cmpxchg we need to keep the original value in RAX | ||
| assert(node->GetRegNum() == REG_RAX); |
There was a problem hiding this comment.
Nothing was stopping LSRA from changing the definition register on this node before, it just never happened because LSRA was preferring to change the user instead.
| assert(node->GetRegNumByIdx(0) == REG_EAX); | ||
| assert(node->GetRegNumByIdx(1) == REG_EDX); |
There was a problem hiding this comment.
There is a "new" invariant that LSRA does not change definition registers on multireg nodes, since that would require a parallel store that is very hard to accomplish for codegen without a temporary. E.g. in this case we could need to do (eax, edx) = (edx, eax).
The only benefit is that it saves insertion of some explicit copies, but the trade off is an implicit contract between LSRA and codegen that is not very faithful. When LSRA defines in a different register than the constrained register, it believes that codegen can insert a copy and simultaneously use it from the new location, which is not true. This difference in the model has to be handled by inserting additional kill constraints during LSRA build, for example for shifts on xarch.
This PR additionally fixes a few issues uncovered while making this change:
GT_CATCH_ARGcodegen was straight up wrong, not properly producing its register valueFix #125432
Fix #125685