Description
Description
A recent Xamarin iOS PR (dotnet/macios#18742) ran into an issue in .NET 8/Mono with the RuntimeTypeHandle.Equals
method.
The Xamarin tooling generates several lookup functions in IL using Mono.Cecil which look something like this:
public uint LookupTypeId(RuntimeTypeHandle handle) {
if (handle.Equals(typeof(NSString).TypeHandle)) {
return 1u;
}
if (handle.Equals(typeof(NSSet).TypeHandle)) {
return 2u;
}
// ...
return unchecked((uint)-1);
}
While this worked fine in .NET 7, in .NET 8 calling for example LookupTypeId(typeof(NSString).TypeHandle)
won't return the correct value (4294967295
instead of 1
).
Reproduction Steps
The issue can be reproduced with a modified HelloWorld Mono sample:
<!-- src/mono/sample/LookupTable/LookupTable.ilproj -->
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
</PropertyGroup>
</Project>
// src/mono/sample/LookupTable/LookupTable.il
.assembly extern mscorlib{}
.assembly LookupTable{}
.class public auto ansi abstract sealed beforefieldinit LookupTable
extends [mscorlib]System.Object
{
.method public hidebysig static
int32 LookupTypeId (
valuetype [mscorlib]System.RuntimeTypeHandle handle
) cil managed
{
.maxstack 8
IL_aaaa: ldarga.s handle
ldtoken [mscorlib]System.String
call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle)
brfalse.s IL_aaab
ldc.i4.2
ret
IL_aaab: ldc.i4.0
ret
}
}
<!-- src/mono/sample/HelloWorld/HelloWorld.csproj -->
<ItemGroup>
<ProjectReference Include="..\LookupTable\LookupTable.ilproj" />
</ItemGroup>
// src/mono/sample/HelloWorld/Program.cs
if (LookupTable.LookupTypeId(typeof(string).TypeHandle) == 2) {
System.Console.WriteLine("FOUND");
} else {
System.Console.WriteLine("NOT FOUND");
}
Build the mono runtime with: ./build.sh mono+libs -c Release
Build and run the sample with mono -C src/mono/sample/HelloWorld MONO_CONFIG=Release MONO_ARCH=arm64 clean run
Expected behavior
The program should print FOUND
.
Actual behavior
The program prints NOT FOUND
.
Regression?
The generated lookup tables worked well before bumping to .NET 8.
Known Workarounds
When we disable inlining of the RuntimeTypeHandle.Equals
method in src/mono/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs
, the lookup function works correctly:
[MethodImpl(MethodImplOptions.NoInlining)]
public bool Equals(RuntimeTypeHandle handle)
{
return value == handle.Value;
}
Configuration
- .NET 8 (
e9ce3aac5440b8d5b76bddfea6285e546083589d
) - building on macOS 13.5
- ARM64 (M1)
- Specific to
Release
configuration
Other information
@filipnavara managed to workaround the issue by preventing inlining of the Equals
method and so we believe that the culprit is inlining.