La seguente C# funzione:Qual è lo scopo di extra ldnull e tail. in F # implementazione vs C#?
T ResultOfFunc<T>(Func<T> f)
{
return f();
}
compila sorprende a questo:
IL_0000: ldarg.1
IL_0001: callvirt 05 00 00 0A
IL_0006: ret
Ma l'equivalente F # funzione:
let resultOfFunc func = func()
compila a questo:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: tail.
IL_0005: callvirt 04 00 00 0A
IL_000A: ret
(Entrambi sono in modalità di rilascio). C'è un extra nop all'inizio che non sono troppo curioso, ma la cosa interessante sono le istruzioni aggiuntive ldnull
e tail.
.
mia ipotesi (probabilmente sbagliato) è che ldnull
è necessaria nel caso in cui la funzione è void
quindi ritorna ancora qualcosa (unit
), ma questo non spiega qual è lo scopo dell'istruzione tail.
. E cosa succede se la funzione spinge qualcosa in pila, non è bloccato con un valore null extra che non viene visualizzato?
Sospetto che in questo caso la funzione sia stata convertita in una chiamata di coda: la nuova funzione ottiene lo spazio di stack del precedente –
Nota che questo potrebbe causare una pessimizzazione delle prestazioni, vedere questa domanda: [Prestazioni penalità quando Generic.List .Add è l'ultima istruzione in una funzione e l'ottimizzazione di tailcall è attiva] (http://stackoverflow.com/q/28649422/636019) –
ildjarn