2016-05-17 11 views
5

Questa domanda è nel contesto della scrittura di un compilatore C per una CPU homebrew a 16 bit.Quale è più utile a livello di assembly, 64 registri o tre istruzioni per l'operando?

Ho 12 bit di operando per le istruzioni ALU (come ADD, SUB, AND, ecc.).

Potrei dare istruzioni tre operandi da 16 registri o due operandi da 64 registri.

ad es.

SUB A <- B - C (registers r0-r15) 

vs

SUB A <- A - B (registers r0-r63) 

sono sedici registri, con le istruzioni a tre operandi, più utile di 64 registri con le istruzioni di due operandi, ai compilatori C e ai loro autori?

+0

Solo in un primo momento (in x86 mi dispiace solo l'assemblea che conosco). La maggior parte dei programmi che ho messo in atto come IDA di solito usano registri Eax attraverso Edx, quindi è 4. Quindi hai Ebp ed Esp così 6. Eip non dovrebbe aver bisogno dell'ALU. Eflags (di nuovo senza bisogno di ALU) ESI ed EDI fanno 8. Quindi, da un primo pensiero, non credo che la maggior parte dei programmi utilizzi più di 16 registri. Potrei mancarne alcuni, ma penso che un buon primo test di sanità mentale per determinare ciò riguarderebbe cosa gcc compila e trova se utilizza anche più di 16 registri sull'ALU. – arduic

+0

Dove stai andando a codificare la modalità di indirizzamento? –

+1

@WeatherVane È RISC: carichi e negozi sono operazioni esplicite con il proprio codice operativo. È una CPU homebrew - le uniche modalità di indirizzamento per carichi e negozi sono offset immediati a 8 bit da zero, da PC o da un altro registro. – fadedbee

risposta

4

16 registri con istruzioni 3-operando non distruttive è probabilmente migliore.

Tuttavia, si dovrebbe anche considerare di fare qualcos'altro di interessante con quei bit di istruzioni. Per l'homebrew, probabilmente non ti interessa prenotare alcuno per future estensioni, e non vuoi aggiungere una tonnellata di opcode extra (like PPC does).

ARM prende l'approccio interessante di avere un operando per ogni istruzione passare attraverso the barrel shifter, quindi ogni istruzione è un'istruzione "shift-and-any" gratuitamente. Questo è supportato anche in modalità "thumb", dove le istruzioni più comuni sono solo 16 bit.(Modo ARM ha RISC 32bit tradizionale formato istruzione fisso. Si dedica 4 su quei bit di esecuzione predicato per ogni istruzione.)


Mi ricordo di vedere uno studio sui guadagni Potenza dal raddoppiare il numero di registri in un architettura teorica, per SPECint o qualcosa del genere. 8-> 16 era forse 5 o 10%, 16-> 32 era solo un paio% e 32-> 64 era ancora più piccolo.

Quindi 16 registri integer sono "sufficienti" la maggior parte del tempo, a meno che non si stia lavorando con int32_t molto, poiché ogni valore avrà due registri a 16 bit. x86-64 ha solo 16 registri GP, e la maggior parte delle funzioni può mantenere molto del loro stato live in registri in modo abbastanza confortevole. Anche nei loop che effettuano chiamate di funzione, nell'ABI ci sono abbastanza registri preservati dalle chiamate che spesso non si verificano nel circuito.

I guadagni in termini di dimensioni del codice e conteggio delle istruzioni da 3 operandi saranno maggiori rispetto a quelli che si verificano con lo spargimento/ricarica occasionale. l'output gcc deve sempre essere mov e utilizzare lea come add-shift non distruttivo.


Se si desidera ottimizzare la CPU per il software-pipelining per nascondere la latenza carico di memoria (which is simpler than full out-of-order execution), più registri sono grandi, esp. se non si ha il cambio del nome. Tuttavia, non sono sicuro di quanto siano buoni i compilatori allo static instruction scheduling. Non è più un argomento caldo, dal momento che tutte le CPU ad alte prestazioni sono fuori uso. (OTOH, un sacco di software che le persone effettivamente utilizzano è in esecuzione su CPU ARM in ordine negli smartphone.) Non ho esperienza nel cercare di ottenere i compilatori per ottimizzare le CPU in ordine, quindi IDK quanto sia fattibile dipendere da quella.

Se la tua CPU è così semplice da non poter fare altro mentre un carico è in volo, questo probabilmente non ha importanza. (Questo sta diventando davvero mano mossi perché io non ne so abbastanza su ciò che è pratico per un design semplice. Anche "semplice" in-order sono pipeline moderne CPU.)


64 registri è entrare in "troppo molti "territori, dove salvarli/ripristinarli richiede molto codice. La quantità di memoria è probabilmente ancora trascurabile, ma dal momento che non è possibile eseguire il loop dei registri, sono necessarie 64 istruzioni.


Se stai progettando un ISA da zero, uno sguardo ai Agner Fog's CRISC proposal e la discussione risultante. I tuoi obiettivi sono molto diversi (CPU a 64 bit con prestazioni elevate e risparmio energetico rispetto a 16 bit semplici), quindi i tuoi ISA saranno ovviamente molto diversi. Tuttavia la discussione può farti pensare a cose che non hai considerato, o idee che vuoi provare.

+0

Molto interessante vedere Fog distillare le sue conoscenze in un concetto architettonico. Sii gentile se riuscisse a formalizzarlo fino al punto in cui i simulatori potrebbero essere realizzati, come il MMIX di Knuth. Insieme ai registri cache/debug/fault, ecc. Sembra che manchi ancora un documento definitivo ... –

+0

@BrettHale: Non ho esaminato la versione corrente della proposta. Uno dei post più recenti sul thread di discussione è stato che Agner sta lavorando all'assembler e al supporto del simulatore per questo e cose del genere, ma che non ha molto tempo da dedicare a quel lavoro. x86 potrebbe non durare per sempre, e sarebbe davvero necessario se l'architettura "open source" con i vettori progettati fin dall'inizio ha preso il sopravvento. –

2

Per quanto riguarda la quantità di registri, in generale penso che la maggior parte di C possa essere compilata con un codice macchina efficiente quando sono disponibili solo 16 registri di uso generale (come AMD64). Tuttavia, potrebbe essere utile avere un paio di registri dedicati per gli argomenti delle funzioni e alcuni contrassegnati come volatili - il che significa che possono essere utilizzati all'interno di qualsiasi funzione, ma potrebbero essere danneggiati da qualsiasi funzione chiamata. Aumentare a 32 registri potrebbe essere utile, ma dubito che molti miglioreranno se tu avessi 64 registri general purpose per una normale CPU a 16-bit. Dovrai comunque salvare il contenuto originale della maggior parte dei registri che utilizzerai nella tua funzione C in pila. Limitare una funzione per usare solo 7 registri simultaneamente (piuttosto che 37) potrebbe essere ancora più (stack) efficiente per un compilatore C, anche quando sono disponibili molti più registri.

Molto dipende dal C calling convention che verrà utilizzato. Quali registri devono essere usati per passare i valori dal chiamante al chiamato, quali registri devono essere considerati volatili, qual è il costo di spingere a/popping dallo stack, ecc. Potresti vincere di più usando un Register Window per gestire i tuoi registri e impilare l'utilizzo tra le chiamate di funzione. Per esempio, Sun Sparc ha una finestra di registro di 8 registri completamente "locali", 8 registri che sono condivisi con il chiamante e 8 registri che saranno condivisi con qualsiasi funzione di chiamata. (Inoltre possono essere indirizzati anche 8 registri globali.) In questo modo non devi preoccuparti di spingere in pila, ci sarà sempre una singola pressione di 16 registri per ogni chiamata di funzione simultaneamente alla modifica del puntatore di esecuzione e un 16 registrati pop per ogni ritorno. Intel ia64 ha qualcosa di simile ma con una dimensione della finestra di registro configurabile.

Tuttavia, lo ha un leggero vantaggio rispetto a SUB A,B quando la conservazione dei risultati intermedi è davvero importante (A deve essere conservata spesso) e un semplice registro per registrare la copia è considerevolmente costoso. Questo sembra improbabile nella maggior parte dei casi.

E userete registri separati a virgola mobile o fissa?

Problemi correlati