-
Notifications
You must be signed in to change notification settings - Fork 5k
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_UNUSED
flag and helper methods (IsUnused
,SetUnused
) forGenTreeRetExpr
. - Updates
IndirectCallTransformer
and the inline substitution walker to convert unused or voidGT_RET_EXPR
s into NOPs or extract side effects. - Refines IR validation and side‐effect analysis to skip over
GT_COMMA(NOP,NOP)
patterns and nestedGT_RET_EXPR
chains.
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
IsCommaNop
is defined below as a nested function, but C++ does not allow nested function definitions. MoveIsCommaNop
out 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
while
loop never updatespotentialCall
, leading to an infinite loop whenever the initial node isGT_RET_EXPR
. Consider changing it back toif
or updatingpotentialCall
within 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_EXPR
s 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.