NOP en versión de lanzamiento de código F #
-
06-07-2019 - |
Pregunta
Estoy jugando con F # en VS2010 beta2, y como soy nuevo en F #, simplemente elegí uno de los ejemplos comunes y seguí adelante e implementé una función factorial como:
let rec factorial n =
if n <= 1 then 1 else n * factorial (n - 1);;
Si compilo esto y miro el código generado en Reflector, obtengo el código C # correspondiente:
public static int Factorial(int n) {
if (n <= 1)
return 1;
return n * Factorial(n - 1);
}
Entonces, si compilo la representación en C # de Reflector del código F #, esperaría obtener una IL idéntica.
Sin embargo, si compilo estos dos fragmentos en modo de lanzamiento y comparo el IL generado, son diferentes (son funcionalmente idénticos, pero aún así difieren un poco).
La implementación de C # se compila para:
.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
}
La implementación de F # se compila para:
.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
}
El código generado es idéntico, excepto para diferentes maxstack y la instrucción NOP adicional en el método F #.
Probablemente esto no sea significativo, pero tengo curiosidad por saber por qué el compilador F # insertaría NOP en una versión de lanzamiento.
¿Alguien puede explicar por qué?
(Estoy perfectamente consciente de que el compilador de F # no ha pasado por el mismo nivel de prueba del mundo real que el compilador de C #, pero esto es tan obvio que podría haberlo detectado).
EDITAR: el comando de compilación es el siguiente
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
(ensamblados referenciados eliminados por brevedad).
Solución
La diferencia de maxstack se debe al hecho de que el compilador de C # compila el primer método con un encabezado del cuerpo del método «light», que se usa cuando el código es pequeño, no tiene excepciones y no tiene locales. En ese caso, el maxstack no se especifica y el valor predeterminado es 8.
El compilador F # está utilizando un encabezado del cuerpo del método «fat», y especifica el maxstack que ha calculado.
En cuanto al nop, es porque estás compilando en modo de depuración. Siempre comienzan un cuerpo de método con un nop. Ver en 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);
Si compilo su factorial sin símbolos de depuración, no obtengo un nop.