Skip to content

[Mono] RuntimeTypeHandle.Equals seems to be broken in Release mode #90800

Closed
@simonrozsival

Description

@simonrozsival

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.

cc @rolfbjarne @ivanpovazan @filipnavara

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions