Track return type for shared generics via exact context - #129373
#129373Track return type for shared generics via exact context#129373MichalPetryka wants to merge 6 commits into
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR extends CoreCLR JIT “exact context” tracking so that calls with shared generic return types can later recover a more precise return type (avoiding __Canon), enabling improved downstream optimizations (e.g., devirtualization / inlining decisions based on sharper types).
Changes:
- Introduces
ExactContextInfo(renamed fromLateDevirtualizationInfo) and stores it onGT_CALLnodes for virtual calls and certain shared-generic-return calls. - Uses the saved exact context in
gtGetClassHandleto query the EE for a more precise call return type. - Adjusts NativeAOT/CoreCLR tooling JitInterface logic around method signature instantiation and callsite-specific intrinsic expansion context.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/methodtable.cpp | Adds a reverse hierarchy lookup when retrieving parent instantiation. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Changes how memberParent is handled when computing a method signature. |
| src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | Updates contextHandle when an intrinsic expands to a different callsite-specific helper method. |
| src/coreclr/jit/inline.h | Renames LateDevirtualizationInfo → ExactContextInfo and updates associated comment. |
| src/coreclr/jit/importercalls.cpp | Saves ExactContextInfo for virtual calls and shared generic returns. |
| src/coreclr/jit/gentree.h | Renames the per-call field to gtExactContextInfo. |
| src/coreclr/jit/gentree.cpp | Initializes/clones gtExactContextInfo and uses it to refine return-type signature queries. |
| src/coreclr/jit/fginline.cpp | Updates late devirtualization to use gtExactContextInfo. |
Comments suppressed due to low confidence (1)
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:1233
- This change avoids the Debug.Assert by skipping re-instantiation when memberParent isn't the same type definition as method.OwningType. However, RyuJIT can pass a derived type as memberParent (e.g., Derived when the method is declared on Base), and in that case we still need to locate the corresponding instantiation of the owning type up the BaseType chain to produce the correct signature. Otherwise the returned signature can remain canonical/shared (e.g., with __Canon) and defeat the purpose of passing memberParent.
else if (type is InstantiatedType instantiatedType && method.OwningType.HasSameTypeDefinition(instantiatedType))
{
Instantiation methodInst = method.Instantiation;
method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), instantiatedType);
if (methodInst.Length > 0)
{
method = method.MakeInstantiatedMethod(methodInst);
}
}
Update the exact context info after late devirtualization
|
@MihuBot -nuget |
Co-authored-by: Steve <hez2010@outlook.com>
|
@MihuBot -nuget -jitutils-repo EgorBo/jitutils -jitutils-branch pmi-deterministic-cctors |
|
The bot shows a bunch of diffs in NuGet libraries but only 2 ones in the BCL so it seems like the BCL just doesn't hit this pattern? @AndyAyersMS does this answer your question from the last time? |
|
Workflow state for the Holistic Review Orchestrator. {
"version": 5,
"last_dispatched_commit": "e91c12de49c642b60b9be49c91e8421c1c289596",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "f36379692fdd0448b5b1276a1a4aa075a0d2c1e0",
"last_reviewed_commit": "e91c12de49c642b60b9be49c91e8421c1c289596",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "f36379692fdd0448b5b1276a1a4aa075a0d2c1e0",
"last_recorded_worker_run_id": "29676143510",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "e91c12de49c642b60b9be49c91e8421c1c289596",
"review_id": 4730524423
}
]
} |
There was a problem hiding this comment.
Holistic Review
Motivation: When the JIT queries a call's return type via gtGetClassHandle for a user call, it previously discarded the exact generic context, so for shared generic instantiations it could only recover an approximate (canonical) return type. This blocks type-driven optimizations on values flowing out of such calls. The PR revives the earlier attempt (#100041) to preserve the exact context so the precise return type is available. Example improvement: (compilerexplorer.com/redacted)
Approach: The existing per-call LateDevirtualizationInfo side structure is generalized and renamed to ExactContextInfo (gtLateDevirtualizationInfo -> gtExactContextInfo). It is now populated not only for devirtualization candidates but also for calls whose signature return type is a shared instantiation (eeIsSharedInst(sig->retTypeSigClass), excluding byrefs). A new eeGetMethodFromContext helper factors the method-context decoding out of eeGetClassFromContext. gtGetClassHandle consumes the saved context to compute an exactClass (and, for method contexts, an exact method) before calling eeGetMethodSig, yielding the precise return type. The RyuJit AOT getCallInfo path is updated to set contextHandle when intrinsic-for-callsite expansion swaps the target method, so the JIT sees a consistent exact context in that case.
Summary: The change is well-scoped and internally consistent. The rename is applied uniformly (no stale LateDevirtualizationInfo references remain), gtExactContextInfo is initialized in gtNewCallNode and copied in gtCloneExprCallHelper, and the unconditional deref in LateDevirtualization remains safe because the field is still always set for devirtualization candidates. All new reads outside that path are null-guarded, and eeGetMethodFromContext asserts its context encoding. The main residual risk is correctness of the newly-derived exact return types across the full generics matrix (CoreCLR and NativeAOT) — the author noted encountering asserts while iterating — so this is primarily validated by broad JIT/pri1 and NativeAOT CI coverage rather than by review of the diff alone. No blocking issues found in the changed lines. Verdict: LGTM pending green CI.
Detailed Findings
No actionable defects identified in the changed lines. Observations:
gtGetClassHandle(src/coreclr/jit/gentree.cpp): for a CLASS-flagged context,methodremains the (canonical)gtCallMethHndwhileexactClassis set from the context; for a METHOD-flagged context bothmethodandexactClassare refined. This asymmetry is intentional and correct, but relies oneeGetMethodSigproducing the exact return type from the approximate method + exact class in the CLASS case; worth confirming via CI on shared value-type instantiations.- The
sig->retType != CORINFO_TYPE_BYREFexclusion inimportercalls.cppis a reasonable guard (byref-returning shared generics don't yield a usable return class), but is undocumented; a brief comment on why byref is excluded would aid future readers. - The NativeAOT
getCallInfochange asserts!exactContextNeedsRuntimeLookupand no instantiations on the intrinsic-expansion swap path; these assumptions look sound for theExpandIntrinsicForCallsitecases but are the most AOT-specific part of the change and should be exercised by the NativeAOT test legs.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 69.6 AIC · ⌖ 10.5 AIC · ⊞ 10K
Retrying #100041, seems to work locally but not sure if everything is correct since I was getting a bunch of asserts.
Example code this improves: https://compiler-explorer.com/z/q437fGGnW