Skip to content

Latest commit

 

History

History
38 lines (23 loc) · 3.53 KB

readytorun-pinvoke.md

File metadata and controls

38 lines (23 loc) · 3.53 KB

P/invoke marshalling pregeneration in ReadyToRun

One of the themes for .NET 5 is improvement of startup times for apps running on top of the CoreCLR runtime. One of the costs that apps must pay at startup is the generation of p/invoke interop marshalling stubs.

P/invoke marshalling is the process of converting managed types declared in the signature of a p/invoke into native types expected by the foreign native function targeted by the p/invoke. Marshalling helps with e.g. bridging the gap between managed methods that model text strings as System.String instances and native methods that expect text strings to be a zero-terminated buffer of Utf-8 characters.

P/invoke marshalling is performed by methods called "marshalling stubs" that the runtime generates based on the signature of the p/invoke, MarshalAs attributes applied to the method parameters, and other attributes that control p/invoke marshalling.

These stubs can be generated ahead of time, as part of ReadyToRun compilation.

P/invoke marshalling pregeneration

The marshalling code generator that the runtime currently uses is written in C++ and is tightly coupled with the CoreCLR type system. As such, it would require a non-trivial effort to re-use the existing marshallers in the ReadyToRun compiler that is written in C#.

Marshalling code generators will therefore have to be duplicated in C#. To limit the amount of duplication, we’ll be scoping down the number of supported marshallers to the most common ones. Uncommon marshallers (StringBuilder, UnmanagedType.AsAny, HandleRef, Decimal, DateTime, interface, etc.) will stay JITted at runtime.

We’ll be reusing a subset of the marshalling code generator written for the CoreRT AOT compiler by making it work on CoreCLR and bringing it to production quality.

Following marshallers will be supported:

  • Primitive types, enums, blittable structs
  • Pointers and references to primitive types, enums, and blittable structs
  • Arrays of primitive types and arrays of blittable structs
  • String (Ansi, Unicode, Utf8)
  • SafeHandle
  • Delegate

All the other marshallers will be generated and JITted at runtime.

The IL stubs generated by the runtime are currently shared by all p/invokes that have the same signature/marshalling. As part of this work, we’re going to make p/invoke stubs like any other methods. This will enable their inlining.

Expected limitations

In the first phase, p/invoke pregeneration will be limited to CoreLib and large version bubble modes that include CoreLib. There are several reasons for this limitation – all of these limitations need to be lifted to enable p/invoke pregeneration outside large version bubble that includes CoreLib:

  1. The marshallers often need to call helpers defined in CoreLib. Outside large version bubble mode, the ReadyToRun compiler can currently only reference members defined in other assemblies if there’s an existing MemberRef record in the IL metadata stream. It’s not possible to generate fixups referencing methods the module didn’t already reference.
  2. The helpers defined in CoreLib do not represent public APIs. Their shape and existence is not guaranteed in future versions of the runtime. We need to make these helpers public APIs.
  3. Versioning of the generated marshallers: the generation of marshalling code is tied to the version of the runtime. ReadyToRun code can possibly be used on multiple different versions of the runtime. We need to make sure the code that the runtime generates if e.g. ReadyToRun is turned off is the same as the code we generated ahead of time.