JIT: Remove boxing from value type instance calls#130871
Conversation
Readds the optimization that we removed in 3206a8e, this time applying the optimization only when permitted via detecting `RefSafetyRulesAttribute` and `UnscopedRef`.
|
Azure Pipelines: Successfully started running 6 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR reintroduces a JIT optimization to eliminate boxing for certain value-type instance calls by allowing the JIT to ask the runtime whether the value-type receiver (this) can escape the callee, based on RefSafetyRulesAttribute opt-in and [UnscopedRef] annotations.
Changes:
- Adds a new JIT↔EE interface method
canValueClassInstancePointerEscape(and updates versioning, thunks, wrappers, SuperPMI recording/replay). - Implements CoreCLR + managed (R2R/NativeAOT tooling) logic to determine escape eligibility using module-scoped
RefSafetyRulesAttributeand method-scopedUnscopedRefAttribute. - Updates the JIT importer to replace eligible heap boxes with a stack-local copy when calling an unboxed entry point, and tracks a new JIT metric for this optimization.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/wellknownattributes.h | Adds UnscopedRef / RefSafetyRules to well-known attribute handling (incl. R2R awareness asserts). |
| src/coreclr/vm/jitinterface.cpp | Implements CEEInfo::canValueClassInstancePointerEscape and module opt-in parsing via RefSafetyRulesAttribute. |
| src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp | Records/replays the new JIT↔EE API call in SuperPMI. |
| src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp | Forwards the new API in the simple shim. |
| src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp | Adds call counting for the new API in the counter shim. |
| src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp | Records the new API result in the collector shim. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h | Adds MethodContext packet + API record/replay declarations for the new call. |
| src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp | Implements MethodContext record/dump/replay storage for the new call. |
| src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h | Adds an LWM map for CanValueClassInstancePointerEscape. |
| src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt | Adds the new interface method to thunk generation input. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | Implements the managed-side escape check using metadata attributes. |
| src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs | Wires the new callback through the generated unmanaged entrypoints. |
| src/coreclr/tools/aot/jitinterface/jitinterface_generated.h | Adds the new callback/wrapper for the AOT JIT interface wrapper. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs | Ensures [UnscopedRef] is tracked in the R2R attribute presence table. |
| src/coreclr/jit/jitmetadatalist.h | Adds a new metric counter for “removed box” devirtualized calls. |
| src/coreclr/jit/importercalls.cpp | Uses the new escape query to replace eligible heap boxes with a local copy when calling unboxed entrypoints. |
| src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp | Adds wrapper plumbing for the new interface method. |
| src/coreclr/jit/ICorJitInfo_names_generated.h | Adds the method name entry for the interface. |
| src/coreclr/jit/gentree.cpp | Extends gtTryRemoveBoxUpstreamEffects with BR_MAKE_LOCAL_COPY transformation support. |
| src/coreclr/jit/compiler.h | Adds BR_MAKE_LOCAL_COPY option to BoxRemovalOptions. |
| src/coreclr/inc/jiteeversionguid.h | Bumps the JIT↔EE interface GUID for versioning. |
| src/coreclr/inc/icorjitinfoimpl_generated.h | Adds the new method declaration to the generated EE-side impl header. |
| src/coreclr/inc/corinfo.h | Adds the new ICorStaticInfo::canValueClassInstancePointerEscape API contract. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/coreclr/jit/gentree.cpp:16859
- When converting the box temp local from
TYP_REFto a struct local,lvaSetStructis invoked withunsafeValueClsCheckforced tofalse. This can skip theCORINFO_FLG_UNSAFE_VALUECLASScheck and fail to mark the local as an unsafe buffer / require a GS cookie, which is a correctness+security issue for value types flagged as unsafe value classes.
Use unsafeValueClsCheck: true here so the JIT preserves existing GS cookie behavior for such structs.
|
Azure Pipelines: Successfully started running 6 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
cc @dotnet/jit-contrib PTAL @AndyAyersMS @jkotas This was mostly written by Copilot (reverting the removal and the VM/crossgen2 attribute parsing part). |
|
I take it SPC has already opted into ref safety? It does. Looks like Roslyn adds this. MihuBot (or similar) should show diffs in the key async methods we're targeting. |
|
How often is JIT going to ask this question? Wondering if these two attribute lookups would need to be promoted into something that the type system knows about and caches as bits. |
Readds the optimization that we removed in
3206a8e, this time applying the optimization only when permitted by looking for
RefSafetyRulesAttributeandUnscopedRef.Depends on #130863
Fix #130185
Diff for `AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted`