Skip to content

Comments

Enable R2R precompilation of objc_msgSend P/Invoke stubs#124770

Draft
davidnguyen-tech wants to merge 6 commits intodotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs
Draft

Enable R2R precompilation of objc_msgSend P/Invoke stubs#124770
davidnguyen-tech wants to merge 6 commits intodotnet:mainfrom
davidnguyen-tech:feature/r2r-objc-pinvoke-stubs

Conversation

@davidnguyen-tech
Copy link
Member

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

Problem

TODO: Add measurements on a sample app

The R2R PInvokeILEmitter previously threw NotSupportedException for any P/Invoke requiring an ObjC pending exception check (objc_msgSend calls), forcing runtime IL stub generation every time.

On JIT-less platforms like iOS with CoreCLR, these stubs fall back to the interpreter, with unnecessary performance costs.

Solution

Change R2R P/Invoke IL Emitter to emit a call to ObjectiveCMarshal.ThrowPendingExceptionObject() after the native call, mirroring the NativeAOT PInvokeILEmitter:

if (MarshalHelpers.ShouldCheckForPendingException(context.Target, _pInvokeMetadata))
{
MetadataType lazyHelperType = context.SystemModule.GetKnownType("System.Runtime.InteropServices.ObjectiveC"u8, "ObjectiveCMarshal"u8);
callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(lazyHelperType
.GetKnownMethod("ThrowPendingExceptionObject"u8, null)));

For blittable ObjC P/Invokes, the entire stub is now precompiled into the R2R image.

davidnguyen-tech and others added 5 commits February 20, 2026 12:57
The R2R PInvokeILEmitter previously threw NotSupportedException for any
P/Invoke requiring an ObjC pending exception check (objc_msgSend family),
forcing runtime IL stub generation. On JIT-less platforms like iOS with
CoreCLR, these stubs fall back to the interpreter.

This change teaches the R2R emitter to emit a call to
ObjectiveCMarshal.ThrowPendingExceptionObject() after the native call,
mirroring the common/NativeAOT PInvokeILEmitter. For blittable ObjC
P/Invokes, the entire stub is now precompiled into the R2R image.

The ThrowPendingExceptionObject method was previously only available in
NativeAOT's System.Private.CoreLib. This change adds it to CoreCLR's
ObjectiveCMarshal.CoreCLR.cs as well, delegating to the existing
StubHelpers.GetPendingExceptionObject() infrastructure.

Changes:
- ObjectiveCMarshal.CoreCLR.cs: Add ThrowPendingExceptionObject()
- PInvokeILEmitter.cs: Remove NotSupportedException for ObjC; emit
  ThrowPendingExceptionObject call after the native call
- Marshaller.ReadyToRun.cs: Add IsObjCMessageSendPInvoke helper
- GeneratesPInvoke: Return true for ObjC P/Invokes
- CreateValueFromKey: Catch RequiresRuntimeJitException for ObjC
  P/Invokes with non-blittable params
- CorInfoImpl: Remove assert that assumed GeneratesPInvoke is false
  when stubIL is null

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove Marshaller.IsObjCMessageSendPInvoke wrapper, call
  MarshalHelpers.ShouldCheckForPendingException directly in GeneratesPInvoke
- Clarify GeneratesPInvoke comment: IsMarshallingRequired returns true for
  ObjC P/Invokes because they need an IL stub, not actual type marshalling
- Add comment to empty catch block explaining JIT fallback behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
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
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants