Skip to content

ILLink is removing branch in System.SR.GetResourceString #1112

@eerhardt

Description

@eerhardt

In the dotnet/runtime libraries, we have the following code in System.SR:

        // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format.
        // by default it returns false.
        // Native code generators can replace the value this returns based on user input at the time of native code generation.
        // Marked as NoInlining because if this is used in an AoT compiled app that is not compiled into a single file, the user
        // could compile each module with a different setting for this. We want to make sure there's a consistent behavior
        // that doesn't depend on which native module this method got inlined into.
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static bool UsingResourceKeys() => false;

        internal static string GetResourceString(string resourceKey, string? defaultString = null)
        {
            if (UsingResourceKeys())
            {
                return defaultString ?? resourceKey;
            }

            // do the resource look-up

In the 3.1 libraries, the above code would produce the following IL:

.method assembly hidebysig static string 
        GetResourceString(string resourceKey,
                          [opt] string defaultString) cil managed
{
  .param [2] = nullref
  // Code size       49 (0x31)
  .maxstack  2
  .locals (string V_0)
  IL_0000:  call       bool System.SR::UsingResourceKeys()
  IL_0005:  brfalse.s  IL_000e
  IL_0007:  ldarg.1
  IL_0008:  dup
  IL_0009:  brtrue.s   IL_000d
  IL_000b:  pop
  IL_000c:  ldarg.0
  IL_000d:  ret
  IL_000e:  ldnull
  IL_000f:  stloc.0
  .try
  {
    IL_0010:  call       class [System.Resources.ResourceManager]System.Resources.ResourceManager System.SR::get_ResourceManager()

However, in the latest 5.0 builds, this is the IL:

.method assembly hidebysig static string 
        GetResourceString(string resourceKey,
                          [opt] string defaultString) cil managed
{
  .param [2] = nullref
  // Code size       42 (0x2a)
  .maxstack  2
  .locals (string V_0)
  IL_0000:  call       bool System.SR::UsingResourceKeys()
  IL_0005:  brfalse.s  IL_0007
  IL_0007:  ldnull
  IL_0008:  stloc.0
  .try
  {
    IL_0009:  call       class [System.Runtime]System.Resources.ResourceManager System.SR::get_ResourceManager()

Note that we are still issuing the call to System.SR::UsingResourceKeys(), but we are stripping out the branch code that will return defaultString ?? resourceKey;.

The above SR code is what we have in place for being able to remove resource strings on UWP. I was attempting to flip the hard-coded bool to true to test out how much size we could save if we used the same approach. However, since ILLink is running during our library build (before we try trimming for size in the context of an app), it is causing the pattern to no longer work.

Maybe ILLink should respect the [MethodImpl(MethodImplOptions.NoInlining)] attribute when it looks for constant method bodies, and not try to fold them?

cc @marek-safar @vitek-karas @MichalStrehovsky @sbomer

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions