Par curiosité j'ai essayé de générer un appel tail opcode à l'aide de C#. Fibinacci est facile, donc mon exemple en c# ressemble à ceci:
private static void Main(string[] args)
{
Console.WriteLine(Fib(int.MaxValue, 0));
}
public static int Fib(int i, int acc)
{
if (i == 0)
{
return acc;
}
return Fib(i - 1, acc + i);
}
Si je le construire dans la libération et de l'exécuter sans débogage je ne suis pas un débordement de pile. Le débogage ou le courant sans optimisations et je dois obtenir un débordement de la pile, ce qui implique que la queue d'appel est de travailler quand dans la version avec des optimisations (qui est ce que j'attendais).
Le MSIL pour cette ressemble à ceci:
.method public hidebysig static int32 Fib(int32 i, int32 acc) cil managed
{
// Method Start RVA 0x205e
// Code Size 17 (0x11)
.maxstack 8
L_0000: ldarg.0
L_0001: brtrue.s L_0005
L_0003: ldarg.1
L_0004: ret
L_0005: ldarg.0
L_0006: ldc.i4.1
L_0007: sub
L_0008: ldarg.1
L_0009: ldarg.0
L_000a: add
L_000b: call int32 [ConsoleApplication2]ConsoleApplication2.Program::Fib(int32,int32)
L_0010: ret
}
Je l'ai attendu de voir une queue opcode, par la msdn, mais il n'est pas là. Cela m'a demande si le compilateur JIT a été chargé de mettre là? J'ai essayé de ngen l'assemblée (à l'aide d' ngen install <exe>
, dans la windows assemblées liste pour l'obtenir) et la charge en ILSpy mais c'est la même chose pour moi:
.method public hidebysig static int32 Fib(int32 i, int32 acc) cil managed
{
// Method Start RVA 0x3bfe
// Code Size 17 (0x11)
.maxstack 8
L_0000: ldarg.0
L_0001: brtrue.s L_0005
L_0003: ldarg.1
L_0004: ret
L_0005: ldarg.0
L_0006: ldc.i4.1
L_0007: sub
L_0008: ldarg.1
L_0009: ldarg.0
L_000a: add
L_000b: call int32 [ConsoleApplication2]ConsoleApplication2.Program::Fib(int32,int32)
L_0010: ret
}
Je n'ai toujours pas le voir.
Je sais que F# poignées d'appel tail bien, j'ai donc voulu comparer ce F# a fait avec ce que le C# n'. Mon F# exemple ressemble à ceci:
let rec fibb i acc =
if i = 0 then
acc
else
fibb (i-1) (acc + i)
Console.WriteLine (fibb 3 0)
Et l'généré IL pour le fib méthode ressemble à ceci:
.method public static int32 fibb(int32 i, int32 acc) cil managed
{
// Method Start RVA 0x2068
// Code Size 18 (0x12)
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = { int32[](Mono.Cecil.CustomAttributeArgument[]) }
.maxstack 5
L_0000: nop
L_0001: ldarg.0
L_0002: brtrue.s L_0006
L_0004: ldarg.1
L_0005: ret
L_0006: ldarg.0
L_0007: ldc.i4.1
L_0008: sub
L_0009: ldarg.1
L_000a: ldarg.0
L_000b: add
L_000c: starg.s acc
L_000e: starg.s i
L_0010: br.s L_0000
}
Qui, selon ILSpy, est équivalent à ceci:
[Microsoft.FSharp.Core.CompilationArgumentCounts(Mono.Cecil.CustomAttributeArgument[])]
public static int32 fibb(int32 i, int32 acc)
{
label1:
if !(((i != 0)))
{
return acc;
}
(i - 1);
i = acc = (acc + i);;
goto label1;
}
Donc F# générés queue appel à l'aide des instructions goto? Ce n'est pas ce à quoi je m'attendais.
Je n'essaie pas de s'appuyer sur la queue appeler de n'importe où, mais je suis juste curieux de savoir où est-ce que opcode préparez-vous? Comment est C# fait-il cela?