Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLR hosting #9572

Merged
merged 143 commits into from
Feb 28, 2025
Merged

CLR hosting #9572

merged 143 commits into from
Feb 28, 2025

Conversation

grendello
Copy link
Contributor

@grendello grendello commented Dec 2, 2024

Implements the bulk of CoreCLR host support but does not enable
building of the runtime. Runtime build will be enabled in a separate
PR, which will also be used to fix and update tests, potentially
introducing new ones. This is in the interest to keep this PR as
small as possible and getting the changes that affect the whole build
system into main as quickly as possible.

@grendello grendello force-pushed the dev/grendel/clr-host branch 3 times, most recently from c085352 to 13bbd93 Compare December 3, 2024 21:15
@grendello grendello force-pushed the dev/grendel/clr-host branch 2 times, most recently from 82cfb8a to 77aa40a Compare December 4, 2024 22:10
@jonathanpeppers
Copy link
Member

Ok, here is what we should do for MSBuild properties for now:

  • $(_AndroidRuntime) is the property we use throughout Android project builds (customer projects), it can say MonoVM, NativeAOT, or CoreCLR. It is a private property.
  • If PublishAot=true, $(_AndroidRuntime)=NativeAOT
  • If UseMonoRuntime=false, $(_AndroidRuntime)=CoreCLR
  • If $(UseMonoRuntime)="", we default to $(UseMonoRuntime)=true in DefaultProperties.targets
  • If UseMonoRuntime=true, $(_AndroidRuntime)=MonoVM

We could remove $(_AndroidNativeAot) and check '$(_AndroidRuntime)' == 'NativeAOT' instead. If we keep it, we should introduce $(_AndroidCoreClr).

@grendello
Copy link
Contributor Author

Ok, here is what we should do for MSBuild properties for now:

* `$(_AndroidRuntime)` is the property we use throughout Android project builds (customer projects), it can say `MonoVM`, `NativeAOT`, or `CoreCLR`. It is a private property.

* If `PublishAot=true`, `$(_AndroidRuntime)=NativeAOT`

* If `UseMonoRuntime=false`, `$(_AndroidRuntime)=CoreCLR`

* If `$(UseMonoRuntime)=""`, we default to `$(UseMonoRuntime)=true` in `DefaultProperties.targets`

* If `UseMonoRuntime=true`, `$(_AndroidRuntime)=MonoVM`

We could remove $(_AndroidNativeAot) and check '$(_AndroidRuntime)' == 'NativeAOT' instead. If we keep it, we should introduce $(_AndroidCoreClr).

I like $(_AndroidRuntime) for this, it's much leaner and reads better IMO :)

jonathanpeppers added a commit that referenced this pull request Jan 15, 2025
Context: #9572 (comment)
Context: https://github.com/xamarin/xamarin-macios/blob/2009c571aa8a975ab0e0b1785e5484dbd64d6f9b/dotnet/targets/Xamarin.Shared.Sdk.targets#L1004-L1006

To align with xamarin/xamarin-macios, the following public MSBuild
properties can be used to select a runtime for iOS, macOS, etc.:

* `$(UseMonoRuntime)=true`: MonoVM
* `$(UseMonoRuntime)=false`: CoreCLR
* `$(PublishAot)=true`: NativeAOT
* Defaults if blank, select MonoVM

Introduce a private `$(_AndroidRuntime)` property we can use
throughout our build to know which runtime is being targetted.

This way, if a new property is designed in the future, we can keep
using `$(_AndroidRuntime)` and simply update the defaults.
jonpryor pushed a commit that referenced this pull request Jan 21, 2025
…#9686)

Context: #9572 (comment)
Context: https://github.com/xamarin/xamarin-macios/blob/2009c571aa8a975ab0e0b1785e5484dbd64d6f9b/dotnet/targets/Xamarin.Shared.Sdk.targets#L1004-L1006

To align with xamarin/xamarin-macios, the following public MSBuild
properties can be used to select a runtime for iOS, macOS, etc.:

  * `$(UseMonoRuntime)=true`: MonoVM
  * `$(UseMonoRuntime)=false`: CoreCLR
  * `$(PublishAot)=true`: NativeAOT
  * Defaults if blank, select MonoVM

Introduce a private `$(_AndroidRuntime)` property we can use
throughout our build to know which runtime is being targeted.

This way, if a new property is designed in the future, we can keep
using `$(_AndroidRuntime)` and simply update the defaults.
jonpryor pushed a commit that referenced this pull request Feb 6, 2025
Context: #9572

#9572 is prototyping CoreCLR support, and as part of
that it renamed e.g. `src/native/monodroid` to
`src/native/mono/monodroid` -- "inserting" a "runtime" value
underneath `src/native` -- so that CoreCLR code won't be intermingled
with MonoVM code.

Separate out these file moves into a separate PR, to reduce the size
and review complexity of #9572.
jonpryor pushed a commit that referenced this pull request Feb 14, 2025
Context: #9572

Add support for Unicode strings to the LLVM IR generator, together with
de-duplication and support for outputting the same string encoded both
as UTF-8 and Unicode (UTF16LE).

Additionally, since all the files are generated from separate tasks, we
don't have a global LLVM IR state which can keep track of strings and
ensure that there are no duplicate symbol names.  To prevent potential
clashes, each generator now sets the default string group name which is
unique for each module.

	; From marshal_methods.arm64-v8a.ll
	@.mm.0 = dso_local constant [102 x i8] c"Android.App.Activity, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065\00", align 1

	; from environment.arm64-v8a.ll
	@.env.0 = dso_local constant [7 x i8] c"normal\00", align 1

	; from typemaps.arm64-v8a.ll
	@.tmr.0 = dso_local constant [22 x i8] c"android/os/BaseBundle\00", align 1

`mm`, `env`, and `tmr` are the default string groups for each module.

In the future, we should try to manage strings globally (which would
also result in more de-duplication).
jonpryor pushed a commit that referenced this pull request Feb 14, 2025
Context: #9572

Add a new `src/native/common` for files which can be shared between
MonoVM and CoreCLR, and move common infrastructural source there,
such as `jni-wrappers.hh` and `strings.hh`.

Copy the parts of `src/native/mono` that will be used by #9572 but
require modification to be used with CoreCLR into `src/native/clr`.

`src/native/clr` is not yet compiled; this merely serves the purpose
of making #9572 smaller and easier to review, copying code from the
existing MonoVM implementation to the CoreCLR one, mostly without
functional changes.  There are very few functional changes, most code
is copied verbatim with the only changes being formatting, function
declaration syntax and, sometimes, the class in which the code lives.

Only the code that's actually used in #9572 is copied.  This allows
us to refresh our runtime code while cleaning it up at the same time,
with the goal of having an implementation that is tailored strictly
towards CoreCLR (which doesn't have e.g. Mono's embedding APIs)
grendello added a commit that referenced this pull request Feb 18, 2025
grendello added a commit that referenced this pull request Feb 19, 2025
grendello added a commit that referenced this pull request Feb 20, 2025
grendello added a commit that referenced this pull request Feb 20, 2025
This is needed until #9572 is merged, since the != 'NativeAOT'
would enable the guarded code to run also for `CoreCLR`, which
is not fully functional in this PR.
@grendello grendello force-pushed the dev/grendel/clr-host branch from af8bb41 to c853ea1 Compare February 21, 2025 08:48
jonpryor added a commit that referenced this pull request Feb 21, 2025
Context: #9572

PR #9572 is quite large.  Extract out a set of changes for easier
review.

Co-authored-by: Jonathan Pryor <jonpryor@vt.edu>
Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
@grendello grendello force-pushed the dev/grendel/clr-host branch from b9bd06b to ee0c8a5 Compare February 21, 2025 16:47
…)"

This reverts commit dfa3046.

Breaks MSBuild:

MSBUILD : error MSB1025: An internal failure occurred while running MSBuild.
Microsoft.Build.Framework.InternalErrorException: MSB0001: Internal MSBuild Error: Missing resource 'RestoreComplete'
   at Microsoft.Build.Shared.ErrorUtilities.ThrowInternalError(String message, Object[] args)
   at Microsoft.Build.Shared.ErrorUtilities.VerifyThrow(Boolean condition, String unformattedMessage, Object arg0)
   at Microsoft.Build.Shared.AssemblyResources.GetStringFromEngineResources(String name)
   at Microsoft.Build.Logging.TerminalLogger.ProjectFinished(Object sender, ProjectFinishedEventArgs e)
   at Microsoft.Build.BackEnd.Logging.EventSourceSink.RaiseEvent[TArgs](TArgs buildEvent, ArgsHandler`1 handler, ArgsHandler`1 followUpHandler)
   at Microsoft.Build.BackEnd.Logging.EventSourceSink.RaiseEvent[TArgs](TArgs buildEvent, ArgsHandler`1 handler, ArgsHandler`1 followUpHandler)
   at Microsoft.Build.BackEnd.Logging.LoggingService.RouteBuildEvent(BuildEventArgs eventArg)
   at Microsoft.Build.BackEnd.Logging.LoggingService.RouteBuildEvent(Object loggingEvent)
   at Microsoft.Build.BackEnd.Logging.LoggingService.LoggingEventProcessor(Object loggingEvent)
This reverts commit 73d0588.

Seems to break Aot
@grendello grendello force-pushed the dev/grendel/clr-host branch from 1d48832 to 473814d Compare February 28, 2025 12:07
@jonpryor jonpryor merged commit ea38c0c into main Feb 28, 2025
1 of 21 checks passed
@jonpryor jonpryor deleted the dev/grendel/clr-host branch February 28, 2025 14:26
jonpryor pushed a commit that referenced this pull request Feb 28, 2025
…9805)

Context: #9572
Context: a760281
Context: 8bc7a3e

Commit 8bc7a3e (among others) introduced "LLVM Marshal Methods",
which uses LLVM-IR to create [Java Native Method Names][0] which,
when invoked, uses the MonoVM embedding API to resolve a function
pointer to an `[UnmanagedCallersOnly]` "marshal method":

	using get_function_pointer_fn = void (*) (int assemblyIndex, int classIndex, int methodIndex, void*& target);
	static get_function_pointer_fn get_function_pointer;

	extern "C" JNIEXPORT void
	xamarin_app_init (void *env, get_function_pointer_fn p)
	{
	  if (p == nullptr) {abort (…);}
	  get_function_pointer = p;
	}

	using android_app_activity_on_create_bundle_fn = void (*) (JNIEnv *env, jclass klass, jobject 	savedInstanceState);
	static android_app_activity_on_create_bundle_fn android_app_activity_on_create_bundle = nullptr;
	
	extern "C" JNIEXPORT void
	JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) noexcept
	{
	  if (android_app_activity_on_create_bundle == nullptr) {
	    get_function_pointer (
	        16,                                                               // mono image index; computed at build time
	        0,                                                                // class index; computed at build time
	        0x0600055B,                                                       // method token; computed at build time
	        reinterpret_cast<void*&>(android_app_activity_on_create_bundle)   // target pointer
	    );
	  }
	
	  android_app_activity_on_create_bundle (env, klass, savedInstanceState);
	}

The downside with all this is that `get_function_pointer` -- backed
by `MonodroidRuntime::get_function_pointer()` -- used the MonoVM
embedding API, which means it isn't compatible with CoreCLR.

Implement a fully managed backend for `get_function_pointer`, inspired
by the [dotnet/macios "ManagedStaticRegistrar"][1], which can be used
by both MonoVM and CoreCLR.  It can be used by setting the
`$(_AndroidUseManagedMarshalMethodsLookup)` MSBuild property to true,
alongside `$(AndroidEnableMarshalMethods)`=true.

`$(_AndroidUseManagedMarshalMethodsLookup)` is False by default.

The managed backend for `get_function_pointer` is
`Java.Interop.ManagedMarshalMethodsLookupTable.GetFunctionPointer()`,
which works with the assistance of a linker step which generates IL
for several lookup tables as part of `MarshalMethodsAssemblyRewriter`.

Each `[UnmanagedCallersOnly]` marshal method is given three indexes:
`(uint assemblyIndex, uint classIndex, uint methodIndex)`, which are
also baked into the generated LLVM-IR in
`MarshalMethodsNativeAssemblyGenerator`.


The indexes are used in three different switches generated in different
parts of the app:

 1. A `<JI>GetFunctionPointer(int methodIndex)` method is added to
    each class which has `[UnmanagedCallersOnly]` marshal methods.

      - The method contains a simple switch over `methodIndex` and
        returns the unmanaged function pointer to the given UCO.

 2. `Java.Interop.__ManagedMarshalMethodsLookupTable__` public class,
    generated by the `ManagedMarshalMethodsLookupGenerator`, is added
    to each assembly containing types containing marshal methods, and
    contains a `GetFunctionPointer(int classIndex, int methodIndex)`.

      - The method contains a simple switch over `classIndex`, looks
        up the corresponding type

      - Special care is given to calling
        `<JI>GetFunctionPointer(int methodIndex)` on private nested
        classes (and also `protected` and `private protected`).
        These methods cannot be called directly due to visibility
        constraints, so additional proxy method(s) are added to their
        parent class(es) to forward the calls to the nested classes.

      - If there are too many classes form a given assembly with UCOs,
        this switch might be too long for JIT to compile.  To solve
        this problem, we would need to split the method into several
        smaller ones, as described in [ManagedStaticRegistrar][1].
        I have not implemented this yet, since I am not convinced we
        will run into the same problem as on iOS, where the UCO methods
        each have just a single `int` index and are initially part of
        one huge switch statement, while we are already splitting the
        lookup into three levels by the assembly and class.

 3. The method
    `Java.Interop.ManagedMarshalMethodsLookupTable.GetFunctionPointer(int assemblyIndex, int classIndex, int methodIndex)`
    within `Mono.Android.dll` is *replaced* by the linker.

      - The method contains a simple switch over `assemblyIndex` and
        calls methods from (2).

During app startup when `$(_AndroidUseManagedMarshalMethodsLookup)`
is true, we P/Invoke into `xamarin_app_init()`, providing it
`Java.Interop.ManagedMarshalMethodsLookupTable.GetFunctionPointer()`.
This allows the existing *form* of LLVM-IR Marshal Methods to be
reused in a CoreCLR context.

[0]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
[1]: https://github.com/dotnet/macios/blob/97fbaaf403322c655a16267c10bf52fe6cdb712c/docs/managed-static-registrar.md#method-mapping
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants