Skip to content

Enable R2R precompilation of blittable ObjC P/Invokes#124770

Draft
davidnguyen-tech wants to merge 14 commits into
dotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs
Draft

Enable R2R precompilation of blittable ObjC P/Invokes#124770
davidnguyen-tech wants to merge 14 commits into
dotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs

Conversation

@davidnguyen-tech
Copy link
Copy Markdown
Member

@davidnguyen-tech davidnguyen-tech commented Feb 23, 2026

Description

Enables ReadyToRun precompilation of blittable objc_msgSend-family P/Invokes on Apple platforms. The R2R-emitted stub now calls a managed ObjectiveCMarshal.ThrowPendingExceptionObject() helper right after the native call, so ObjC P/Invokes no longer have to fall back to JIT/interpreter for the pending-exception check.

Changes

  • Adds ObjectiveCMarshal.ThrowPendingExceptionObject() in CoreLib and exposes it to the runtime binder via corelib.h.
  • R2R PInvokeILEmitter emits a call to that helper for ObjC P/Invokes, spilling the native return value across the call so the IL evaluation stack is empty at the helper site.
  • Marshaller.ReadyToRun no longer rejects ObjC P/Invokes, and PInvokeILStubMethodIL.IsMarshallingRequired is the single place that gates the ObjC pending-exception check.
  • ReadyToRunCodegenCompilation pre-registers ObjectiveCMarshal / ThrowPendingExceptionObject references with the manifest before fixup emission, mirroring the existing AddNecessaryAsyncReferences pattern. Required in non-composite mode (the default on iOS), where CoreLib is an external reference and would otherwise get rejected as out-of-bubble.
  • New R2R test src/tests/readytorun/ObjCPInvokeR2R/ asserts both that the stubs land in the crossgen2 .map (MethodWithGCInfo) and that the pending-exception path actually executes by hijacking MessageSendFunction.MsgSend with a managed callback.

Impact

Synthetic ObjC P/Invoke benchmark on osx-arm64 Release: a console host invokes 100 distinct blittable objc_msgSend P/Invokes per iteration.

Metric Base With PR Delta
Total time for 100 ObjC P/Invoke calls 7.728 ms 1.961 ms -5.77 ms (-75%)
Time spent JIT'ing the P/Invoke stubs 5.163 ms 4.031 ms -1.13 ms (-22%)
P/Invoke stubs AOT-compiled into R2R 0 / 100 100 / 100 +100

MAUI template app on device doesn't provide measurable improvements because ObjC P/Invokes on the MAUI startup path are not the dominant cost.

Copilot AI review requested due to automatic review settings February 23, 2026 22:47
@davidnguyen-tech davidnguyen-tech self-assigned this Feb 23, 2026
Copy link
Copy Markdown
Contributor

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

Enables ReadyToRun (R2R) precompilation of Objective-C objc_msgSend-family P/Invoke stubs by emitting a managed pending-exception check after the native call, avoiding a runtime-stub/interpreter fallback on JIT-less Apple platforms.

Changes:

  • Add ObjectiveCMarshal.ThrowPendingExceptionObject() in CoreCLR and expose it to the runtime binder.
  • Update the R2R PInvokeILEmitter to emit a post-call pending-exception check instead of rejecting ObjC P/Invokes.
  • Adjust R2R compilation policy and IL caching behavior to allow ObjC P/Invokes to be attempted and to gracefully fall back when unsupported.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/vm/corelib.h Adds CoreLib binder entry for ObjectiveCMarshal.ThrowPendingExceptionObject.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs Simplifies P/Invoke stub IL retrieval logic now that unsupported cases are handled/cached in ILCache.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs Emits ThrowPendingExceptionObject after ObjC native calls; removes ObjC-specific NotSupportedException.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs Allows GeneratesPInvoke for ObjC msgSend P/Invokes even when marshalling is otherwise considered required.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs Caches “requires runtime JIT” decisions for unsupported P/Invoke stub emission via RequiresRuntimeJitException catch.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs Implements ThrowPendingExceptionObject() using pending-exception storage and ExceptionDispatchInfo.Throw.

@davidnguyen-tech davidnguyen-tech changed the title Enable R2R precompilation of ObjC objc_msgSend P/Invoke stubs Enable R2R precompilation of objc_msgSend P/Invoke stubs Feb 23, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to 'os-ios': @vitek-karas, @kotlarmilos, @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

@BrzVlad
Copy link
Copy Markdown
Member

BrzVlad commented Feb 24, 2026

Were there also any interpreted pinvokes with non-blittable types ? In case we might need additional fixes, related to the original plan of using LibraryImport.

@AaronRobinsonMSFT
Copy link
Copy Markdown
Member

Do we need to re-enable or create a new sets of tests for this? We have tests for these APIs, src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI, but I don't know if we've disabled them for R2R (it doesn't look like it), so are we missing them during R2R testing somehow?

/cc @jkoritzinsky

@jkoritzinsky
Copy link
Copy Markdown
Member

Do we need to re-enable or create a new sets of tests for this? We have tests for these APIs, src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI, but I don't know if we've disabled them for R2R (it doesn't look like it), so are we missing them during R2R testing somehow?

The tests there are falling back to JIT/Interpreter for these stubs (which is correct behavior). We could add testing to validate that the methods were actually generated (src/tests/readytorun/tests/test.cs and the mainv1.csproj project file have examples of the logic (pass the --map option when running cg2 and then validate the expected entries exist as part of running the test).

PureWeen added a commit to davidnguyen-tech/runtime that referenced this pull request Mar 11, 2026
Analyzes reviewer feedback from BrzVlad, AaronRobinsonMSFT, and jkoritzinsky.
Includes recommended code changes and test coverage plan.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit to PureWeen/runtime that referenced this pull request Mar 13, 2026
…ning findings

- Add LibraryImport vs DllImport analysis explaining why switching macios
  wouldn't solve the R2R precompilation issue
- Add P/Invoke inlining call chain trace showing CorInfoImpl.ReadyToRun.cs
  path (not VM dllimport.cpp) for R2R marshalling decisions
- Document graceful fallback behavior for unsupported marshalling types
- Confirm BrzVlad's approach is both cleaner and more efficient

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 13, 2026 16:29
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment thread pr-124770-plan.md Outdated
- Document new 3-commit structure and PInvokeILStubMethodIL mechanism
- Mark BrzVlad's recommendations as implemented (GeneratesPInvoke reverted,
  ShouldCheckForPendingException removed from IsMarshallingRequired)
- Add open concerns: code size, P/Invoke frame correctness, non-void IL
  stack risk, JIT helper alternative under investigation
- Test coverage still flagged as missing (merge blocker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 17, 2026 19:01
Copy link
Copy Markdown
Contributor

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.

PureWeen and others added 3 commits March 18, 2026 10:51
Adds a ReadyToRun test that validates objc_msgSend P/Invoke stubs are
precompiled into the R2R image using crossgen2's --map output.

The test:
- Declares blittable objc_msgSend DllImport methods
- Compiles them with crossgen2 --map
- At runtime, reads the .map file and asserts P/Invoke stub entries exist
- Only runs on macOS (CLRTestTargetUnsupported for non-Apple platforms)

Addresses reviewer feedback from jkoritzinsky and AaronRobinsonMSFT
requesting test coverage for the R2R ObjC P/Invoke precompilation feature.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Validates that blittable objc_msgSend P/Invoke stubs are precompiled
into the ReadyToRun image by checking the crossgen2 --map output for
MethodWithGCInfo entries.

Key details:
- Uses /usr/lib/libobjc.dylib (exact path matched by ShouldCheckForPendingException)
- Checks specifically for MethodWithGCInfo entries (not just any map reference)
- Requires --inputbubble so crossgen2 can resolve ThrowPendingExceptionObject tokens
- macOS-only (CLRTestTargetUnsupported for non-OSX)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Promote regression test verification to a mandatory section in
  copilot-instructions.md with explicit guidance on using baseline
  build artifacts for negative verification
- Add ReadyToRun/crossgen2 test gotchas section covering --inputbubble,
  map entry semantics, and MarshalHelpers.cs string matching
- Add cross-component build warning (crossgen2 + CoreLib changes)
- Add design doc discoverability instruction
- Strengthen CoreLib rebuild guidance to explain libs.pretest necessity
- Add Step 5 (mandatory negative verification) to jit-regression-test skill
- Create src/tests/readytorun/README.md documenting test patterns,
  map validation rules, version bubble constraints, and platform gating

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 18, 2026 19:44
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/tests/readytorun/README.md Outdated
Comment thread src/tests/readytorun/ObjCPInvokeR2R/ObjCPInvokeR2R.csproj
Comment thread pr-124770-plan.md Outdated
Comment thread pr-124770-analysis.md Outdated
Add P/Invoke marshalling and R2R-specific review rules:
- inputbubble requirement for cross-module CoreLib references
- IsMarshallingRequired prevents JIT from inlining past R2R stubs
- Separation of marshalling vs platform-specific stub requirements
- DllImport detection string matching against MarshalHelpers.cs
- R2R map validation (MethodWithGCInfo vs MethodFixupSignature)
- R2R+NativeAOT P/Invoke emitter alignment checks
- R2R tests need both map validation and runtime verification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos
Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

Copilot AI review requested due to automatic review settings May 18, 2026 13:06
Copy link
Copy Markdown
Contributor

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's findings

  • Files reviewed: 12/12 changed files
  • Comments generated: 7

Comment thread src/tests/readytorun/ObjCPInvokeR2R/ObjCPInvokeR2R.cs Outdated
Comment thread src/tests/readytorun/README.md Outdated
Comment thread pr-124770-plan.md Outdated
Comment thread pr-124770-analysis.md Outdated
Comment thread .github/skills/jit-regression-test/SKILL.md Outdated
Comment thread .github/skills/code-review/SKILL.md Outdated
Comment thread .github/copilot-instructions.md Outdated
kotlarmilos and others added 2 commits May 18, 2026 16:18
- Spill native return value across the ThrowPendingExceptionObject helper
  call so the IL evaluation stack is empty at the call site.
- Add runtime pending-exception validation to the ObjCPInvokeR2R test
  via SetMessageSendCallback + SetMessageSendPendingException.
- Add $(TestLibraryProjectPath) ProjectReference to ObjCPInvokeR2R.csproj
  to match other R2R test projects.
- Remove pr-124770-{plan,analysis}.md scratch notes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 18, 2026 14:21
Copy link
Copy Markdown
Contributor

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's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new

Mirrors the existing AddNecessaryAsyncReferences pattern: when an
ObjC P/Invoke is selected for R2R compilation, the new emitter calls
ObjectiveCMarshal.ThrowPendingExceptionObject() in CoreLib. In
non-composite mode (the default on iOS), CoreLib is an external
reference, so without manifest pre-registration the version-bubble
check in ManifestMetadataTableNode rejects the helper and crossgen2
fails. Uses GetType(throwIfNotFound: false) so non-Apple workloads
(no ObjC interop) skip the lookup harmlessly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-crossgen2-coreclr only use for closed issues os-ios Apple iOS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants