2009-10-23 4 views
6

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

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

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

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

     return n * Factorial(n - 1); 
} 

Так что, если я скомпилировать C# представление рефлектора из F # код, я бы ожидать, чтобы получить идентичную 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#, но это настолько очевидно, что я бы его поймал).

EDIT: Команда компиляции выглядит следующим образом

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 

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

ответ

17

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

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

Что касается 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.

+0

Ну, я использую профиль сборки «release» по умолчанию в VS2010, поэтому я бы предположил, что я на самом деле строю в режиме выпуска. В окне вывода говорится: «Сборка запущена: проект: FSharpLib, Configuration: Release Any CPU». Если я перейду на «отладку», я получу совершенно другой MSIL, как ожидалось. –

+0

Я тестирую с использованием F # 1.9.7.8 и командной строки. Если я не пройду/отлаживаю, я не получу нуль. –

+0

Я обновил вопрос с помощью командной строки для компиляции, и я попытался скомпилировать с помощью fsc.exe напрямую. Тот же результат. Согласно выходному окну VS2010b2 не поставляется с последним компилятором, поскольку он сообщает номер версии F # версии 1.9.7.4. Разве может быть разница? –