NOP в компилация на версия на F# код

Играя с F# във VS2010 beta2 и тъй като съм нов в 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# представянето на F# кода на Reflector, очаквам да получа идентичен 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 

(референтните модули са премахнати за краткост).


person Brian Rasmussen    schedule 23.10.2009    source източник


Отговори (1)


Разликата в 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.

person Jb Evain    schedule 23.10.2009
comment
Е, аз използвам профила за изграждане на изданието по подразбиране във VS2010, така че бих предположил, че всъщност изграждам в режим на издаване. Прозорецът за изход казва, че изграждането е започнало: проект: FSharpLib, конфигурация: освобождаване на всеки процесор. Ако променя на debug, получавам съвсем различен MSIL, както се очаква. - person Brian Rasmussen; 23.10.2009
comment
Тествам с F# 1.9.7.8 и командния ред. Ако не премина /debug, не получавам nop. - person Jb Evain; 23.10.2009
comment
Актуализирах въпроса с командния ред за компилиране и се опитах да компилирам директно чрез fsc.exe. Същият резултат. Според изходния прозорец VS2010b2 не се доставя с най-новия компилатор, тъй като отчита номер на версия на F# версия 1.9.7.4. Възможно ли е това да е разликата? - person Brian Rasmussen; 23.10.2009
comment
Командният ред, който публикувахте, съдържа --debug:pdbonly, което задейства включването на nop. - person Jb Evain; 23.10.2009
comment
Инструкцията NOP IL свързана ли е с JIT към инструкцията за машинен код NOP, или е истинска no-op и се игнорира? - person Lasse V. Karlsen; 23.10.2009
comment
Зависи от изпълнението на JIT. Моно ги игнорира. Мисля, че .net също ги игнорира. - person Jb Evain; 23.10.2009
comment
Само за да бъде ясно, причината, поради която NOP се излъчва във връзка с PDB, е, че за да зададете точка на прекъсване на дебъгера на определени места, имате нужда от инструкция в този момент. Например, в C#, за да зададете точка на прекъсване в }, имате нужда от инструкция за този код, въпреки че кодът не прави нищо. - person Chris Smith; 23.10.2009
comment
Прав си. --debug:pdbonly е виновникът. Все още съм малко изненадан защо е необходим NOP при извеждане на символи, но пасва много добре на цитата от ilxgen.ml. Благодаря за многото актуализации и ви благодаря, че отделихте време. - person Brian Rasmussen; 23.10.2009