Qual è lo scopo del registro del puntatore del frame EBP?

Sono un principiante nel linguaggio assembly e ho notato che il codice x86 emesso dai compilatori di solito mantiene il puntatore del frame anche in modalità release / ottimizzato quando potrebbe usare il registro EBP per qualcos’altro.

Capisco perché il puntatore del frame potrebbe rendere il codice più facile da eseguire il debug e potrebbe essere necessario se alloca() viene chiamato all’interno di una funzione. Tuttavia, x86 ha pochissimi registri e l’utilizzo di due di essi per mantenere la posizione dello stack frame quando uno sarebbe sufficiente non ha senso per me. Perché omettere il puntatore del frame è considerato una ctriggers idea anche nelle build ottimizzate / di rilascio?

Il puntatore del frame è un puntatore di riferimento che consente a un debugger di sapere dove si trova una variabile locale o un argomento con un singolo offset costante. Sebbene il valore di ESP cambi nel corso dell’esecuzione, l’EBP rimane lo stesso, consentendo di raggiungere la stessa variabile allo stesso offset (come il primo parametro sarà sempre a EBP + 8 mentre gli offset ESP possono cambiare in modo significativo dal momento in cui si spinge / cose popping)

Perché i compilatori non buttano via il puntatore del frame? Perché con il puntatore del frame, il debugger può capire dove le variabili e gli argomenti locali stanno usando la tabella dei simboli poiché sono garantiti per essere a un offset costante rispetto a EBP. Altrimenti non esiste un modo semplice per capire dove una variabile locale si trova in un punto qualsiasi del codice.

Come ha detto Greg, aiuta anche ad accumulare lo sbobinamento per un debugger poiché EBP fornisce un elenco di frame di stack collegati in ordine inverso, consentendo al debugger di capire la dimensione dello stack frame (variabili locali + argomenti) della funzione.

La maggior parte dei compilatori offre un’opzione per omettere i puntatori di fotogramma anche se rende il debug molto difficile. Questa opzione non dovrebbe mai essere utilizzata globalmente, nemmeno nel codice di rilascio. Non sai quando sarà necessario eseguire il debug di un arresto anomalo di un utente.

Aggiungendo solo i miei due centesimi a già buone risposte.

Fa parte di una buona architettura del linguaggio avere una catena di frame stack. Il BP punta al frame corrente, dove sono memorizzate le variabili locali di subroutine. (I locali hanno offset negativi e gli argomenti sono in offset positivi.)

L’idea che si stia impedendo l’utilizzo di un registro perfettamente valido nell’ottimizzazione solleva la domanda: dove e quando l’ottimizzazione è effettivamente utile?

L’ottimizzazione vale solo in cicli stretti che 1) non chiamano funzioni, 2) dove il contatore del programma spende una frazione significativa del suo tempo, e 3) nel codice che il compilatore effettivamente vedrà mai (cioè funzioni non librerie). Questa è di solito una frazione molto piccola del codice generale, specialmente nei sistemi di grandi dimensioni.

Un altro codice può essere distorto e schiacciato per eliminare i cicli, e semplicemente non avrà importanza, perché il contatore del programma non è praticamente mai lì.

So che non l’hai chiesto, ma secondo la mia esperienza, il 99% dei problemi di prestazioni non ha nulla a che fare con l’ottimizzazione del compilatore. Hanno tutto a che fare con l’over-design.

Dipende dal compilatore, certamente. Ho visto il codice ottimizzato emesso dai compilatori x86 che utilizza liberamente il registro EBP come registro generale. (Non ricordo quale compilatore l’ho notato con, però.)

I compilatori possono anche scegliere di mantenere il registro EBP per assistere con lo srotolamento dello stack durante la gestione delle eccezioni, ma ancora una volta dipende dall’attuazione precisa del compilatore.

Tuttavia, x86 ha pochissimi registri

Questo è vero solo nel senso che gli opcode possono solo indirizzare 8 registri. Il processore stesso avrà in realtà molti più registri oltre a questo e userà la ridenominazione dei registri, il pipelining, l’esecuzione speculativa e altre parole d’ordine del processore per aggirare tale limite. Wikipedia ha un buon paragrafo introduttivo su cosa può fare un processore x86 per superare il limite del registro: http://en.wikipedia.org/wiki/X86#Current_implementations .

L’uso di frame stack è diventato incredibilmente economico in qualsiasi hardware anche remotamente moderno. Se si dispone di frame stack economici, salvare un paio di registri non è così importante. Sono sicuro che i fotogrammi dello stack veloce rispetto a più registri sono stati un compromesso di ingegneria e sono stati ottenuti frame di stack veloci.

Quanto stai risparmiando andando registro puro? Ne vale la pena?