Skip to content

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

Merged
merged 2 commits into from
May 30, 2025

Conversation

AndyAyersMS
Copy link
Member

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.

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.
@Copilot Copilot AI review requested due to automatic review settings May 30, 2025 00:57
@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 May 30, 2025
@AndyAyersMS
Copy link
Member Author

@EgorBo PTAL
cc @dotnet/jit-contrib

Modest diffs; will link to them when they're up.

Copy link
Contributor

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

Copy link
Contributor

@Copilot Copilot AI left a 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.

@AndyAyersMS AndyAyersMS requested a review from Copilot May 30, 2025 01:11
Copy link
Contributor

@Copilot Copilot AI left a 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) for GenTreeRetExpr.
  • Updates IndirectCallTransformer and the inline substitution walker to convert unused or void GT_RET_EXPRs into NOPs or extract side effects.
  • Refines IR validation and side‐effect analysis to skip over GT_COMMA(NOP,NOP) patterns and nested GT_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. Move IsCommaNop 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 updates potentialCall, leading to an infinite loop whenever the initial node is GT_RET_EXPR. Consider changing it back to if or updating potentialCall within the loop.
while (potentialCall->OperIs(GT_RET_EXPR))

{
JITDUMP("\nmarking RET_EXPR [%06u] as unused\n", dspTreeID(expr));
expr->AsRetExpr()->SetUnused();
}
Copy link
Member

@EgorBo EgorBo May 30, 2025

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);
Copy link
Member

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?

Copy link
Member Author

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.

GenTreeRetExpr* const retExpr = tree->AsRetExpr();
GenTreeCall* const inlineCand = retExpr->gtInlineCandidate;

if ((retExpr->gtFlags & GTF_RET_EXPR_UNUSED) != 0)
Copy link
Member

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 ?

Copy link
Member

@EgorBo EgorBo left a 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

// 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)))
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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...

@AndyAyersMS
Copy link
Member Author

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.

@AndyAyersMS
Copy link
Member Author

@EgorBo I've revised; take another look?

Copy link
Member

@EgorBo EgorBo left a comment

Choose a reason for hiding this comment

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

:shipit:

@AndyAyersMS AndyAyersMS merged commit 2411805 into dotnet:main May 30, 2025
109 checks passed
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.

3 participants