NOP в сборке релиза кода F # - PullRequest
6 голосов
/ 23 октября 2009

Я играю с F # в бета-версии VS2010, и, поскольку я новичок в F #, я просто выбрал один из распространенных примеров и пошел дальше и реализовал функцию факториала как:

let rec factorial n =
  if n <= 1 then 1 else n * factorial (n - 1);;

Если я соберу это и посмотрю на сгенерированный код в Reflector, я получу соответствующий код C #:

public static int Factorial(int n) {
   if (n <= 1) 
      return 1;

      return n * Factorial(n - 1);
}

Так что, если я скомпилирую C # представление Reflector кода F #, я бы ожидал получить идентичный IL.

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

Реализация C # компилируется в:

.method public hidebysig static int32 Factorial(int32 n) cil managed
{
   .maxstack 8
   L_0000: ldarg.0 
   L_0001: ldc.i4.1 
   L_0002: bgt.s L_0006
   L_0004: ldc.i4.1 
   L_0005: ret 
   L_0006: ldarg.0 
   L_0007: ldarg.0 
   L_0008: ldc.i4.1 
   L_0009: sub 
   L_000a: call int32 TestApp.Program::Factorial(int32)
   L_000f: mul 
   L_0010: ret 
}

Реализация F # компилируется в:

.method public static int32 factorial(int32 n) cil managed
{
   .maxstack 5        <=== Different maxstack
   L_0000: nop        <=== nop instruction?
   L_0001: ldarg.0 
   L_0002: ldc.i4.1 
   L_0003: bgt.s L_0007
   L_0005: ldc.i4.1 
   L_0006: ret 
   L_0007: ldarg.0 
   L_0008: ldarg.0 
   L_0009: ldc.i4.1 
   L_000a: sub 
   L_000b: call int32 FSharpModule::factorial(int32)
   L_0010: mul 
   L_0011: ret 
}

Сгенерированный код идентичен, за исключением другого maxstack и дополнительной инструкции NOP в методе F #.

Это, вероятно, несущественно, но мне любопытно, почему компилятор F # вставляет NOP в сборку релиза.

Может кто-нибудь объяснить, почему?

(я прекрасно понимаю, что компилятор F # не прошел тот же уровень тестирования в реальном мире, что и компилятор C #, но это настолько очевидно, что я представляю, что его поймали).

РЕДАКТИРОВАТЬ: команда компиляции выглядит следующим образом

C:\Program Files\Microsoft F#\v4.0\fsc.exe -o:obj\Release\FSharpLib.dll 
--debug:pdbonly --noframework --define:TRACE --optimize+ 
--target:library --warn:3 --warnaserror:76 --vserrors --utf8output --fullpaths 
--flaterrors "C:\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.fs" Module1.fs 

(для краткости ссылочные сборки удалены).

1 Ответ

17 голосов
/ 23 октября 2009

Разница maxstack связана с тем, что компилятор C # компилирует первый метод с заголовком тела метода «light», который используется всякий раз, когда код мал, не имеет исключений и локальных переменных. В этом случае maxstack не указан и по умолчанию равен 8.

Компилятор F # использует заголовок тела метода «fat» и указывает вычисленный им максимальный стек.

Что касается nop, то это потому, что вы компилируете в режиме отладки. Они всегда начинают тело метода с nop. Смотрите из fsharp / ilxgen.ml:

// Add a nop to make way for the first sequence point. There is always such a 
// sequence point even when zapFirstSeqPointToStart=false
do if mgbuf.cenv.generateDebugSymbols  then codebuf.Add(i_nop);

Если я скомпилирую ваш факториал без отладочных символов, я не получу nop.

...