Skip to content
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

Transform indirect calls to direct ones in the importer #85197

Closed
wants to merge 18 commits into from

Conversation

MichalPetryka
Copy link
Contributor

Imports indirect calls as direct ones when the target method is known.

Only handles addresses from ldftn as the VM has no way to verify pointers from static readonly fields and crashes on invalid ones.

The helpers currently contain a small dead path that I'll soon use when extending the code to also handle delegates.

Opening as a draft so that the code can be reviewed while I finish the tests.

Fixes #44610

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 22, 2023
@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Apr 22, 2023
@ghost
Copy link

ghost commented Apr 22, 2023

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

Issue Details

Imports indirect calls as direct ones when the target method is known.

Only handles addresses from ldftn as the VM has no way to verify pointers from static readonly fields and crashes on invalid ones.

The helpers currently contain a small dead path that I'll soon use when extending the code to also handle delegates.

Opening as a draft so that the code can be reviewed while I finish the tests.

Fixes #44610

Author: MichalPetryka
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@AndyAyersMS
Copy link
Member

Some thoughts:

  • I would suggest splitting off the enhanced single-def changes as a separate PR. Ideally this would be a no diff change.
  • The single-def changes (if extended to cover more optimizations) is potentially quite useful on its own; function pointers are pretty rare but other invariant things (say constants) are not.
  • The backwards search for the single def is going to become problematic for throughput; consider instead tracking the def node directly or in a side table. Note cross-block maps will need to be invalidated in certain somewhat rare circumstances (reimportation for example).
  • Or, track which blocks have defs of which locals, so you can avoid searching in cases where the search will fail
  • Or, limit how far back you can search for the def, and limit the how much chaining you allow (hopping from one local to another).

Imports indirect calls as direct ones when the target method is known.

Only handles addresses from ldftn as the VM has no way to verify pointers
from static readonly fields and crashes on invalid ones.

The helpers currently contain a small dead path that I'll soon use when
extending the code to also handle delegates.

Opening as a draft so that the code can be reviewed while I finish the tests.

Fixes dotnet#44610
@MichalPetryka
Copy link
Contributor Author

I'll be unable to respond to reviews for a few weeks, I've asked @thatbakamono to apply any feedback though.

};

GenTree* valueNode = findValue(impStmtList);
BasicBlock* bb = fgFirstBB;
Copy link
Member

Choose a reason for hiding this comment

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

I'm still ambivalent about this non-local searching.

Seems like we could have cases where we are able to do this transformation in Tier0-instr but not in a subsequent OSR, because OSR doesn't have the same fgFirstBB.

I suppose one fix is not to do this transformation in Tier0-instr (though removing the need to have a method probe is appealing)... but only if we can then always do the same in a subsequent rejit.

/*-------------------------------------------------------------------------
* First create the call node
*/
// run transformations when instrumenting to not pollute PGO data
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what you mean by this?

Are you saying if can optimize calli->call here when instrumenting we won't need a method probe? See my note above about whether this is an effective strategy given OSR.

GenTree* fptr = impStackTop().val;
if (fptr->OperIs(GT_LCL_VAR))
{
JITDUMP("impImportCall trying to import calli as call - trying to substitute LCL_VAR\n");
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to know in the dump which local var...


if (valueNode != nullptr && valueNode->OperIs(GT_LCL_VAR))
{
return impGetNodeFromLocal(valueNode);
Copy link
Member

Choose a reason for hiding this comment

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

Seems like this could get tripped up if there is a chain of local assignments where one of them is initialized later than it is used, eg

t0 = t1;
t1 = <fnptr>;
calli t0;

The chained search should start from where the previous search left off.

CORINFO_CLASS_HANDLE targetClass = info.compCompHnd->getMethodClass(replacementMethod);
info.compCompHnd->getMethodSig(replacementMethod, &methodSig, targetClass);

if (methodSig.hasImplicitThis() && targetThis == TYP_UNDEF)
Copy link
Member

Choose a reason for hiding this comment

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

Isn't targetThis always TYP_UNDEF at this point?

CORINFO_METHOD_HANDLE replacementMethod = nullptr;
GenTree* newThis = nullptr;
var_types oldThis = TYP_UNDEF;
var_types targetThis = TYP_UNDEF;
Copy link
Member

Choose a reason for hiding this comment

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

I would rename these to be a bit more descriptive, eg oldThisTyp.

Also move the declaration/definition down closer to where they're used.

}
}

if (valueNode != nullptr && valueNode->OperIs(GT_LCL_VAR))
Copy link
Member

Choose a reason for hiding this comment

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

Add a jit dump here to track the progress of chaining back through other locals.

var_types sourceThis,
var_types targetThis)
{
assert(sourceSig->hasImplicitThis() || sourceThis == TYP_UNDEF);
Copy link
Member

Choose a reason for hiding this comment

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

Seems like sourceThis is always TYP_UNDEF and targetThis is never TYP_VOID?

I can't tell if you are trying to future-proof this method and have it handle cases it currently won't be asked to handle, or if this is a vestige of some earlier version.

Since there is currently just one caller perhaps scope this down to just the cases you'll actually see, and assert if anything else comes along?

//
bool Compiler::impCanSubstituteSig(CORINFO_SIG_INFO* sourceSig,
CORINFO_SIG_INFO* targetSig,
var_types sourceThis,
Copy link
Member

Choose a reason for hiding this comment

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

As above, sourceThisTyp and targetThisType?

Would be good to settle on one pair of adjectives to describe the two options here: one of original/new, old/net, source/target.

@@ -1622,6 +1708,138 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN
#endif // FEATURE_MULTIREG_RET
}

//-----------------------------------------------------------------------------------
// impCanSubstituteSig: Checks whether it's safe to replace a call with another one.
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we're better off implementing this entirely on the VM side of the interface; there's quite a bit of back and forth across the interface here, and the VM may already have something like this that you can just call (though maybe not for "jit sigs")... ?

@ghost ghost closed this Jun 4, 2023
@ghost
Copy link

ghost commented Jun 4, 2023

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@AndyAyersMS AndyAyersMS reopened this Jun 5, 2023
@AndyAyersMS
Copy link
Member

@MichalPetryka plans to work on this soon.

@ghost
Copy link

ghost commented Jul 5, 2023

Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it.

@ghost ghost closed this Jul 5, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Aug 5, 2023
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

optimization of indirect calls
2 participants