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

NativeAOT TODO list #9784

Open
5 of 10 tasks
jonathanpeppers opened this issue Feb 12, 2025 · 7 comments
Open
5 of 10 tasks

NativeAOT TODO list #9784

jonathanpeppers opened this issue Feb 12, 2025 · 7 comments
Assignees
Labels
Area: App+Library Build Issues when building Library projects or Application projects.
Milestone

Comments

@jonathanpeppers
Copy link
Member

jonathanpeppers commented Feb 12, 2025

What do we need to do for NativeAOT?

@jonathanpeppers jonathanpeppers added the Area: App+Library Build Issues when building Library projects or Application projects. label Feb 12, 2025
@jonathanpeppers jonathanpeppers self-assigned this Feb 12, 2025
@dotnet-policy-service dotnet-policy-service bot added the needs-triage Issues that need to be assigned. label Feb 12, 2025
@jonathanpeppers jonathanpeppers removed the needs-triage Issues that need to be assigned. label Feb 12, 2025
@jonathanpeppers jonathanpeppers added this to the .NET 10 milestone Feb 12, 2025
@jonpryor
Copy link
Member

Alternate "MVP 1.1" typemap idea, for posterity: instead of a Dictionary<string, Type>, have two fields:

  1. Dictionary<string, RuntimeTypeHandle> or string[]+RuntimeTypeHandle[] pair, plus
  2. Dictionary<string, Type>

The linker step would populate (1), which might be faster because RuntimeTypeHandle is a stuct.

At runtime, consult (2) first, and if a type mapping doesn't exist, use (1) to lookup the RuntimeTypeHandle + Type.GetTypeFromHandle(RuntimeTypeHandle), caching the result in (2).

Questions:

  • Can we lookup the RuntimeTypeHandle value from Cecil?
  • The caching will require locking. What kind of overhead will this imply?

@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented Feb 12, 2025

There must be a way to use a TypeHandle with Mono.Cecil, as the IL "typemap MVP 1.0" is writing is doing this:

/*
* IL_0000: ldarg.0
* IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IDictionary`2<string, class [System.Runtime]System.Type> Microsoft.Android.Runtime.NativeAotTypeManager::TypeMappings
* IL_0006: ldstr "java/lang/Object"
* IL_000b: ldtoken [Mono.Android]Java.Lang.Object
* IL_0010: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
* IL_0015: callvirt instance void class [System.Runtime]System.Collections.Generic.IDictionary`2<string, class [System.Runtime]System.Type>::Add(!0, !1)
*/
il.Emit (Mono.Cecil.Cil.OpCodes.Ldarg_0);
il.Emit (Mono.Cecil.Cil.OpCodes.Ldfld, field);
il.Emit (Mono.Cecil.Cil.OpCodes.Ldstr, javaKey);
il.Emit (Mono.Cecil.Cil.OpCodes.Ldtoken, module.ImportReference (typeDefinition));
il.Emit (Mono.Cecil.Cil.OpCodes.Call, getTypeFromHandle);
il.Emit (Mono.Cecil.Cil.OpCodes.Callvirt, addMethod);

@filipnavara
Copy link
Member

Ldtoken returns RuntimeTypeHandle so you can use that in the dictionary. It's, however, not a pattern that can be represented in C# and ILSpy will not decode it correctly.

@simonrozsival
Copy link
Member

I'm currently exploring this direction for the type map: https://gist.github.com/simonrozsival/8b3f2294d7fa1cf84ca32e8145984c98

  • there's no startup initialization - the Java class name hashes are stored in a sorted array that's baked into the assembly
  • the runtime type handles are stored in code and are accessed via a jumptable (switch) - this allows us to "serialize" RuntimeTypeHandles in a way which Native AOT understands

What do you think?

@jonathanpeppers
Copy link
Member Author

@simonrozsival brought up a point that the RuntimeTypeHandle values might not match up at runtime in NativeAOT:

  • We run ILLink
  • At some point during/after ILLink, we generate a managed typemap, if we used RuntimeTypeHandle...
  • ILC runs and uses its own set of integers (who knows?) instead of RuntimeTypeHandle

@simonrozsival
Copy link
Member

ILC runs and uses its own set of integers (who knows?) instead of RuntimeTypeHandle

I don't think I expressed myself correctly. If we generate IL that builds the Dictionary<string, RuntimeTypeHandle> using il.Emit (Mono.Cecil.Cil.OpCodes.Ldtoken, module.ImportReference (typeDefinition));, then Native AOT will compile this correctly. That's not the problem.

I was trying to say that we cannot serialize the raw numeric values of RuntimeTypeHandle while we're processing the IL and use it for lookups via Type.GetTypeFromHandle at runtime.

We can of course create an array of RTH, but it will need to be initialized at startup:

static readonly RuntimeTypeHandle[] s_handles = [ typeof(X).TypeHandle, typeof(Y).TypeHandle ];

// is compiled to IL like this:
.method private hidebysig specialname rtspecialname static 
    void .cctor () cil managed 
{
    // Method begins at RVA 0x2058
    // Code size 56 (0x38)
    .maxstack 8

    IL_0000: ldc.i4.2
    IL_0001: newarr [System.Runtime]System.RuntimeTypeHandle
    IL_0006: dup
    IL_0007: ldc.i4.0
    IL_0008: ldtoken X
    IL_000d: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
    IL_0012: callvirt instance valuetype [System.Runtime]System.RuntimeTypeHandle [System.Runtime]System.Type::get_TypeHandle()
    IL_0017: stelem [System.Runtime]System.RuntimeTypeHandle
    IL_001c: dup
    IL_001d: ldc.i4.1
    IL_001e: ldtoken Y
    IL_0023: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
    IL_0028: callvirt instance valuetype [System.Runtime]System.RuntimeTypeHandle [System.Runtime]System.Type::get_TypeHandle()
    IL_002d: stelem [System.Runtime]System.RuntimeTypeHandle
    IL_0032: stsfld valuetype [System.Runtime]System.RuntimeTypeHandle[] X::s_handles
    IL_0037: ret
} // end of method X::.cctor

@filipnavara
Copy link
Member

We can of course create an array of RTH, but it will need to be initialized at startup:

ILC can interpret some static constructors at compile time and produce preinitialized data in the executable image. It would not work for the Dictionary but it can probably work for a simple array... (or can be tweaked to work if it doesn't at the moment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: App+Library Build Issues when building Library projects or Application projects.
Projects
None yet
Development

No branches or pull requests

4 participants