MethodImpl (AggressiveInlining) - насколько он агрессивный?

Итак, я немного посмотрел на MethodImplAttribute, и я наткнулся на MethodImplOptions.AggressiveInlining, который описывается как:

При необходимости метод должен быть встроен.

Ооооо, подумал я сам, это звучит интересно - я могу притворяться, что я умнее, чем компилятор и силовой код, который должен быть встроен, используя только силу моей злой воли и пару квадратных скобок, га, ха, га...

Поэтому я запустил Visual Studio 2013, создал консольное приложение, установил версию .NET на 4.5.1 и написал программу для завершения всех программ (скомпилировав ее в режиме Release, конечно):

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1
{   
    public static class SoHelpful
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetSomeValueProbablyTwelve()
        {
            return 12;
        }
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static int GetSomeValueLikelyThirteen()
        {
            return 13;
        }
        public static int GetSomeValueMaybeItIsTwentyEight()
        {
            return 29;
        }
    }

    class Program
    {       
        static void Main()
        {
            int twelve = SoHelpful.GetSomeValueProbablyTwelve();
            int thirteen = SoHelpful.GetSomeValueLikelyThirteen();
            int twentyNine = SoHelpful.GetSomeValueMaybeItIsTwentyEight();
            Console.WriteLine((twelve + thirteen + twentyNine));
        }
    }
}

Теперь, если я делаю быстрый ildasm, я вижу это:

.class public abstract auto ansi sealed beforefieldinit ConsoleApplication1.SoHelpful
       extends [mscorlib]System.Object
{
  .method public hidebysig static int32  GetSomeValueProbablyTwelve() cil managed
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   12
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueProbablyTwelve

  .method public hidebysig static int32  GetSomeValueLikelyThirteen() cil managed noinlining
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   13
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueLikelyThirteen

  .method public hidebysig static int32  GetSomeValueMaybeItIsTwentyEight() cil managed
  {
    // Code size       3 (0x3)
    .maxstack  8
    IL_0000:  ldc.i4.s   29
    IL_0002:  ret
  } // end of method SoHelpful::GetSomeValueMaybeItIsTwentyEight

} // end of class ConsoleApplication1.SoHelpful

.class private auto ansi beforefieldinit ConsoleApplication1.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main() cil managed
  {
    .entrypoint
    // Code size       29 (0x1d)
    .maxstack  2
    .locals init ([0] int32 twelve,
             [1] int32 thirteen,
             [2] int32 twentyNine)
    IL_0000:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueProbablyTwelve()
    IL_0005:  stloc.0
    IL_0006:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueLikelyThirteen()
    IL_000b:  stloc.1
    IL_000c:  call       int32 ConsoleApplication1.SoHelpful::GetSomeValueMaybeItIsTwentyEight()
    IL_0011:  stloc.2
    IL_0012:  ldloc.0
    IL_0013:  ldloc.1
    IL_0014:  add
    IL_0015:  ldloc.2
    IL_0016:  add
    IL_0017:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_001c:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class ConsoleApplication1.Program

Интересно, поэтому определены три метода:

.method public hidebysig static int32  GetSomeValueLikelyThirteen() cil managed noinlining
.method public hidebysig static int32  GetSomeValueProbablyTwelve() cil managed
.method public hidebysig static int32  GetSomeValueMaybeItIsTwentyEight() cil managed

Таким образом, похоже, что мой агрессивный атрибут inlining теряется.

Я вижу noinlining на GetSomeValueLikelyThirteen, но GetSomeValueProbablyTwelve и GetSomeValueMaybeItIsTwentyEight совпадают.

Итак, что случилось? Я полагаю, что есть несколько возможностей:

  • Компилятор С# понял, что GetSomeValueProbablyTwelve просто не может быть встроен, поэтому не будет беспокоить JIT с моим глупым атрибутом idiocy.
  • Компилятор С# понял, что GetSomeValueMaybeItIsTwentyEight может быть встроен, и поэтому он идентичен в IL как GetSomeValueProbablyTwelve (поэтому мое приписывание в этом случае было также бессмысленным и идиотским).
  • Там какой-то параметр компиляции или что-то, что я должен настроить, чтобы распознать атрибут.
  • Он вообще не реализован в C#.
  • Я нашел ошибку
  • Что-то еще

Итак, кто-нибудь знает, что это такое?

Ответы

Ответ 1

MethodImplAttributes.AggressiveInlining компилируется в флаг в столбце ImplFlags таблицы метаданных MethodDef (ECMA-335 Partition II § 22.26). Значения для этого атрибута перечислены в разделе II § 23.1.11, но AggressiveInlining недокументирован (в таблице для 0x0100 не существует значения).

В процессе компиляции компилятор удаляет сам атрибут из метаданных. Дисассемблер должен реализовать специальную логику, чтобы добавить атрибут обратно, если бит 0x0100 установлен в ImplFlags для метода.