-
Notifications
You must be signed in to change notification settings - Fork 5.2k
JIT: improve codegen for inline candidates called only for effect #116123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT: improve codegen for inline candidates called only for effect #116123
Conversation
Keep track of unused GT_RET_EXPRs, and when we inline, substitute just the return value side effects for unused GT_RET_EXPRs. Reduces address exposure in some GDV cases, which can encourage physical promotion and other good things. In particular one case from dotnet#84872.
|
@EgorBo PTAL Modest diffs; will link to them when they're up. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
There was a problem hiding this comment.
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 enhances JIT inlining to handle GT_RET_EXPR nodes whose return values are unused, by tracking them and substituting side effects only when appropriate.
- Introduces a new
GTF_RET_EXPR_UNUSEDflag and helper methods (IsUnused,SetUnused) forGenTreeRetExpr. - Updates
IndirectCallTransformerand the inline substitution walker to convert unused or voidGT_RET_EXPRs into NOPs or extract side effects. - Refines IR validation and side‐effect analysis to skip over
GT_COMMA(NOP,NOP)patterns and nestedGT_RET_EXPRchains.
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/morph.cpp | Adds IsCommaNop helper and special-cases comma-NOP nodes in tail-call IR validation. |
| src/coreclr/jit/indirectcalltransformer.cpp | Refactors inline-candidate handling to patch unused/void returns. |
| src/coreclr/jit/gentree.h | Defines GTF_RET_EXPR_UNUSED and methods on GenTreeRetExpr. |
| src/coreclr/jit/gentree.cpp | Prints unused markers and updates side-effect detection loop. |
| src/coreclr/jit/fginline.cpp | Enhances placeholder substitution to extract side effects for unused returns. |
| src/coreclr/jit/compiler.hpp | Marks GT_RET_EXPR as unused in gtUnusedValNode. |
Comments suppressed due to low confidence (3)
src/coreclr/jit/morph.cpp:5110
- The helper
IsCommaNopis defined below as a nested function, but C++ does not allow nested function definitions. MoveIsCommaNopout of the method body and declare it as a private member or free function.
else if (IsCommaNop(tree))
src/coreclr/jit/morph.cpp:5123
- This definition appears inside the implementation file rather than at file or class scope, leading to invalid C++ syntax. Refactor it into an appropriate scope (e.g. a private static method of
Compiler).
bool IsCommaNop(GenTree* node)
src/coreclr/jit/gentree.cpp:16886
- This
whileloop never updatespotentialCall, leading to an infinite loop whenever the initial node isGT_RET_EXPR. Consider changing it back toifor updatingpotentialCallwithin the loop.
while (potentialCall->OperIs(GT_RET_EXPR))
src/coreclr/jit/compiler.hpp
Outdated
| { | ||
| JITDUMP("\nmarking RET_EXPR [%06u] as unused\n", dspTreeID(expr)); | ||
| expr->AsRetExpr()->SetUnused(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, it is a bit weird to mutate input/state in gtNew-like APIs (but I think we do that in a few others).
| return false; | ||
| } | ||
|
|
||
| return node->AsOp()->gtGetOp1()->OperIs(GT_NOP) && node->AsOp()->gtGetOp2()->OperIs(GT_NOP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can it be a recursive COMMA(nop, COMMA(nop, nop)) ? perhaps we should use some gtEffectiveVal-like api here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I haven't seen this.
src/coreclr/jit/gentree.cpp
Outdated
| GenTreeRetExpr* const retExpr = tree->AsRetExpr(); | ||
| GenTreeCall* const inlineCand = retExpr->gtInlineCandidate; | ||
|
|
||
| if ((retExpr->gtFlags & GTF_RET_EXPR_UNUSED) != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haven't you just added IsUnused() API ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a few nits
src/coreclr/jit/fginline.cpp
Outdated
| // If this use is an unused ret expr, is the first child of a comma, the return value is ignored. | ||
| // Extract any side effects. | ||
| // | ||
| if (isUnused || ((parent != nullptr) && parent->OperIs(GT_COMMA) && (parent->gtGetOp1() == *use))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the parent check not sufficient? Adding a contextual flag on RET_EXPR seems weird and fragile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point taken, but note RET_EXPRs are already weird and fragile (can't be cloned, etc).
The main motivation was to give the new RET_EXPR created during GDV expansion the same benefit as the original one; in that setting we don't know the RET_EXPR parent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this extra handing in GDV expansion is responsible for about 20% of the diffs in ASP.NET.
I suppose this early in the phase list it's likely the ret expr will be in the next statement. So GDV expansion can search there for its parent; if it fails it can just assume the ret expr is used. I'll give that a try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, I think I understand the GDV case better now. Another +1 for inlining via tree splitting I guess...
|
Revised this to search for the ret expr during GDV instead of marking it up in the importer. Same behavior on the test case and asp.net. |
|
@EgorBo I've revised; take another look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
![]()
Keep track of unused GT_RET_EXPRs, and when we inline, substitute just the return value side effects for unused GT_RET_EXPRs.
Reduces address exposure in some GDV cases, which can encourage physical promotion and other good things.
In particular one case from #84872.