Domanda

Qualcuno può spiegare in queste poche righe di codice MSIL?Perché non sposta di un valore di sconto, la valutazione dello stack di una variabile locale, solo per spostare indietro immediatamente e tornare?

Il seguente codice MSIL carichi di un singolo argomento (una stringa), chiama un metodo che restituisce il valore booleano, e quindi restituisce il valore booleano.Quello che non capisco è il motivo per cui si chiama stloc.0 per memorizzare il valore restituito del metodo in una variabile locale, quindi esegue un esplicito e incondizionato trasferimento di controllo per la successiva riga etichettata (sembra inutile), solo per spostare il valore a destra indietro sulla valutazione dello stack prima di tornare.

.maxstack 1
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string)
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 

La mia ipotesi migliore perché si fa è quello di eseguire una sorta di tipo di controllo per verificare che il valore di una valutazione dello stack è in realtà un valore booleano, prima di tornare.Ma io sono all'oscuro circa l'esplicita saltare alla riga successiva;Voglio dire, non ci vado lo stesso?Il codice sorgente C# per il metodo è solo una linea, che restituisce il risultato del metodo.

È stato utile?

Soluzione

Se si apre questa funzione nel debugger, con il codice compilato in modalità di debug:

bool foo(string arg)
{
    return bar(arg);
}

Ci sono 3 punti di interruzione, è possibile impostare:

  1. Presso la parentesi graffa di apertura della funzione.
  2. Il "ritorno" della linea.
  3. Presso la parentesi graffa di chiusura della funzione.

Impostazione di un punto di interruzione sulla parentesi graffa di apertura significa "rompere quando questa funzione si chiama".Ecco perché non c'è un no-op istruzione all'inizio del metodo.Quando il punto di interruzione è impostato sulla parentesi graffa di apertura il debugger in realtà imposta sul no-op.

Impostazione di un punto di interruzione sulla parentesi graffa di chiusura significa "rompere quando questa funzione termina".A questo proposito, la funzione deve avere una sola istruzione return in IL, in cui il punto di interruzione può essere impostato.Il compilatore permette che utilizzando una variabile temporanea per memorizzare il valore di ritorno, e la conversione

return retVal;

in

$retTmp = retVal;
goto exit;

e poi iniettare il codice riportato di seguito nella parte inferiore del metodo:

exit:
return $ret;

Inoltre, quando in modalità di debug, i compilatori sono stupido il codice su cui generazione.Fondamentalmente fare qualcosa di simile:

GenerateProlog();
foreach (var statement in statements)
{
    Generate(statement);
}
GenerateEpilog();

Nel tuo caso, si stanno vedendo:

return foo(arg);

tradotto in:

; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;

Se il compilatore stava facendo una "finestra scorrevole di ottimizzazione" si potrebbe essere in grado di guardare il codice e rendersi conto che c'era qualche redundency.Tuttavia, compilatori, in genere, non farlo in modalità di debug.Le ottimizzazioni del compilatore può fare cose come eliminare le variabili e istruzioni per il riordino, il che rende difficile il debug.Poiché lo scopo di una build di debug è quello di attivare il debug, non sarebbe un bene per attivare le ottimizzazioni.

In una build di rilascio, il codice non sarà simile a questo.Questo perché il compilatore non introdurre il codice per attivare i punti di interruzione di apertura e di chiusura di parentesi graffe, che lascia solo il seguente deve essere compilata:

return bar(arg);

Che termina abbastanza semplice.

Una cosa da notare, tuttavia, è che non credo che il compilatore C# fa molto scorrevole finestra ottimizzazioni, anche nella build finale.Thats perché la maggior parte di queste ottimizzazioni dipendono sottostante architettura del processore e quindi sono di fatto dal compilatore JIT.Facendo le ottimizzazioni, anche quelli che sono processore agnostico, il compilatore C# può impedire il JIT capacità di ottimizzare il codice (è la ricerca di pattern generati da codice non ottimizzato generazione, e se vede pesantemente ottimizzato IL si può ottenere confuso).Così, di solito manged codice compilatori non li hanno.Si fa un po ' di "cose costose" (che il JIT non vuole fare in fase di esecuzione), come morti codice di rilevamento e di vivere variabile di analisi, ma non risolvono i problemi risolti dalla finestra scorrevole di ottimizzazione.

Altri suggerimenti

Stai compilando in modalità debug o rilascio? In modalità di rilascio ottengo:

.method private hidebysig static bool Test1(string arg) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string)
    L_0006: ret 
}

la ramificazione che stai vedendo è probabilmente per il supporto debugger.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top