2013-03-16 14 views
14

Ho letto il codice sorgente di Clang e ho scoperto qualcosa di interessante sull'ABI di C++ ARM che non riesco a capire per giustificazione. Dalla una versione online del ARM ABI documentation:ARM C++ ABI: valori di ritorno costruttore/distruttore

Questo ABI richiede C1 e costruttori C2 per tornare questo (invece di essere funzioni void) in modo tale che un costruttore C3 può coda chiamare il costruttore C1 e il costruttore C1 può coda chiamare C2.

(e analogamente per i distruttori non virtuali)

io non sono sicuro di quello che C1, C2, e C3 riferimento qui ... questa sezione è destinata ad essere una modifica del §3.1.5 da il generico (cioè Itanium) ABI, ma quella sezione (almeno in this online verison) afferma semplicemente:

Costruttori tornare nulli risultati.

Comunque, io davvero non riesco a capire quale sia lo scopo di questo è: come fa un ritorno costruttore questo consentire l'ottimizzazione chiamata coda, e in quali circostanze?

Per quanto posso dire, l'unica volta che un costruttore potrebbe chiamare un altro con lo stesso valore di ritorno this sarebbe il caso di una classe derivata con una singola classe di base, un corpo di costruttore banale, nessun membro con non banale costruttori e nessun puntatore di tabella virtuale. In realtà, sembra che sarebbe effettivamente più facile, non difficile, ottimizzare con una coda chiamata con un ritorno void, perché allora la restrizione di una singola classe di base potrebbe essere eliminata (nel caso della classe base multipla, il puntatore this restituito dall'ultimo costruttore chiamato non sarà il puntatore this dell'oggetto derivato).

Cosa mi manca qui? C'è qualcosa nella convenzione di chiamata ARM che rende necessario il ritorno this?

+2

Sospetto che C1, C2 e C3 facciano riferimento a "costruttore di oggetti completo", "costruttore di oggetti di base" e "costruttore di allocazione di oggetti completo", su cui è possibile leggere più [qui] (http://stackoverflow.com/ domande/6921295/doppia emissione di costruttori-simboli) – Michael

+0

@Michael ahh, QUESTO ha senso (sapeva che esistevano, ma non conoscevo la convenzione dei nomi storpiati ... Ho assunto 'C1',' C2', e 'C3' si riferiva a costruttori in qualche gerarchia in un diagramma che mancava). Non riesco ancora a capire il caso del distruttore. –

+0

@Michael ok, grazie per il collegamento, capisco anche il caso del distruttore ora, penso ... se nessuno risponde così autorevolmente, risponderò io stesso ad un certo punto –

risposta

8

Ok, Link utili da @ Michael ha fatto questo tutto chiaro ... C1, C2, e C3 fare riferimento al nome-storpiatura del "costruttore completo oggetto", "oggetto di base costruttore", e "oggetto l'assegnazione costruttore completo ", rispettivamente, dal Itanium ABI:

<ctor-dtor-name> ::= C1 # complete object constructor 
        ::= C2 # base object constructor 
        ::= C3 # complete object allocating constructor 
        ::= D0 # deleting destructor 
        ::= D1 # complete object destructor 
        ::= D2 # base object destructor 

il C3/'completo oggetto alloca costruttore' è una versione del costruttore che, piuttosto che operare in deposito già allocato passati ad esso tramite il parametro this, alloca memoria internamente (tramite operator new) e quindi chiama lo C1/"costruttore di oggetti completo", che è il normale costruttore utilizzato per il caso di oggetto completo. Poiché il costruttore deve restituire il puntatore this all'oggetto appena assegnato e costruito, il costruttore C1 deve anche restituire il puntatore this in modo che venga utilizzata una coda.

Il C2/"costruttore di oggetti di base" è il costruttore chiamato dalle classi derivate durante la costruzione di una classe base subobject; la semantica dei costruttori C1 e C2 differisce in caso di ereditarietà virtual e potrebbe essere implementata in modo diverso anche per scopi di ottimizzazione. Nel caso dell'ereditarietà virtual, è possibile implementare un costruttore C1 con chiamate ai costruttori di classe base virtual seguiti da una coda su un costruttore C2, quindi quest'ultimo dovrebbe restituire anche this se il primo lo fa.

Il caso del distruttore è leggermente diverso ma correlato. Come per l'ARM ABI:

Allo stesso modo, abbiamo bisogno di D2 e ​​D1 restituire questo modo che D0 non ha bisogno di salvare e ripristinare questo e D1 possono chiamare coda D2 (se non ci sono le basi virtuali). D0 è ancora una funzione di vuoto.

Il D0/"cancellazione distruttore" viene utilizzato quando si elimina un oggetto, chiama il/"distruttore completo oggetto" D1 e chiede operator delete con il puntatore this successivamente per liberare la memoria. Avere il distruttore D1 restituisce this consente al distruttore D0 di utilizzare il suo valore di ritorno per chiamare operator delete, piuttosto che doverlo salvare in un altro registro o riversarlo in memoria; allo stesso modo, lo D2/"distruttore di oggetti di base" dovrebbe restituire anche this.

L'ARM ABI aggiunge anche:

Non richiediamo thunk a distruttori virtuali per tornare questo. Un tale thunk dovrebbe aggiustare il risultato del distruttore , impedendogli di chiamare il distruttore e annullare qualsiasi possibile salvataggio.

Di conseguenza, solo le chiamate non virtuali dei distruttori D1 e D2 possono essere invocate per restituire questo.

Se ho capito bene, significa che questa ottimizzazione save-restore-elisione può essere utilizzato solo quando D0 chiamate D1 staticamente (vale a dire nel caso di un non virtual distruttore).