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
In the
dotnet/runtimelibraries, we have the following code in System.SR:In the
3.1libraries, the above code would produce the following IL:However, in the latest
5.0builds, this is the IL:Note that we are still issuing the call to
System.SR::UsingResourceKeys(), but we are stripping out the branch code that willreturn 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
trueto 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