perché le architetture della CPU usano un registro di flag (vantaggi?)


15

Alcune CPU hanno un registro flag (ARM, x86, ...), altre no (MIPS, ...). Qual è il vantaggio di avere un'istruzione CMP per aggiornare il registro di flag seguito da un'istruzione di ramo invece di usare un registro zero e rami condizionali per controllare segni, overflow ecc?

Risposte:


11

Nelle moderne micro-architetture con la ridenominazione dei registri, il costo di implementazione delle bandiere o meno è abbastanza simile. La differenza principale che mi viene in mente è che alcuni flag indicano le caratteristiche di un valore (Il valore è negativo? Il valore è zero? Il valore ha parità pari o dispari?), Mentre alcuni rappresentano un evento che si è verificato durante un'operazione precedente (l'istruzione add ha una realizzazione o un overflow?) Ciò ha portato a una situazione tutt'altro che ideale sul MIPS quando si voleva simulare un'aggiunta a 64 bit sull'architettura a 32 bit (o un'aggiunta a 128 bit su Architettura a 64 bit.) Sulla maggior parte delle architetture con una bandiera carry è presente uno specialeadd-with-carryistruzione, che include il flag carry dall'istruzione add precedente. Ciò rende la simulazione dell'aritmetica multi-precisione relativamente poco costosa su molte architetture con i registri delle bandiere.

D'altro canto, testare un registro N-bit per zero o non-zero è in realtà sorprendentemente costoso. Per testare un registro N-bit per zero, è necessario eseguire un'operazione NOR N-bit, che richiede livelli di logica per il calcolo. Sulle architetture con flag registra la logica aggiuntiva per il calcolo zero / non zero alla fine dello stadio ALU può far rallentare il clock (o forzare l'ALU a eseguire due cicli). Per questo motivo, penso, alcuni le architetture, come SPARC, avevano due versioni di ciascuna operazione aritmetica, una che metteva le bandiere e una che no.O(logN)

Ma MIPS non salva nulla qui. Hanno appena spostato il problema altrove. Su MIPS c'è branch-on-equalun'istruzione. Ciò significa che l'istruzione di diramazione deve effettivamente avere uno stadio ALU (includendo qualcosa come un'operazione bit a bit xorseguita da a norper ridurre fino al singolo bit uguale / non uguale) prima di determinare in che direzione va il ramo.

L'architettura DEC Alpha ha cercato di dividere la differenza usando un trucco. DEC Alpha non aveva registri flag, ma non aveva nemmeno branch-on-equalun'istruzione. Invece le istruzioni di diramazione guardano tutte allo stato di un unico registro per tutti gli usi. V'è branch-on-zero, branch-on-not-zero, branch-on-less-than-zero, ecc Il trucco è che si può dare ad ogni General Purpose Register un bit 65 ° in più che ti dice se gli altri 64 bit sono tutti a zero o meno. Questo rende più come avere un registro di flag: tutte le istruzioni di diramazione guardano un singolo bit (che è già calcolato) per prendere la loro decisione, ma ora sei tornato a capire come calcolare quel bit di indicatore zero aggiuntivo durante un normale ALU ciclo. (E non puoi ancora fare l'aritmetica multi-precisione solo guardando la bandiera carry dell'operazione precedente.)


2
Le operazioni di impostazione non CC erano (da quanto ho capito) un'ottimizzazione del compilatore , che consentiva al compilatore di programmare in anticipo le istruzioni di impostazione CC senza che il valore venisse ostacolato da queste ultime istruzioni. Il PowerPC750 posizionava i registri delle condizioni (8 registri a 4 bit) più vicino al front-end in modo tale che un ramo preso colpendo la cache delle istruzioni di destinazione del ramo e avendo la condizione disponibile abbastanza presto potesse risolvere un ramo preso senza penalità. (Il CRISP di AT&T ha anche sfruttato la risoluzione precoce del ramo.) La piccola quantità e la specializzazione dei CC lo rendono più pratico.
Paul A. Clayton,

Un dettaglio: tutti i calcoli dei flag non sono uguali. Immagina che la tua CPU abbia i tradizionali flag NZVC. Se tutte le istruzioni ALU sono autorizzate ad aggiornare i flag, è necessario posizionare la generazione di flag dopo il sommatore / sottrattore e alcuni mux. Il flag negativo è facile, è solo il MSB, mentre il flag zero è costoso e dipende da ogni bit. Ora, se si limitano i flag alle istruzioni di confronto (e bit test), i flag Zero possono essere calcolati con XOR paralleli sugli operandi di origine, senza attendere il risultato della sottrazione. Calcolare la bandiera Z dopo un'aggiunta è quasi inutile.
TEMLIB,

7

1 Dal punto di vista ISA

  1. Avere istruzioni di prova che impostano solo le bandiere è solo un modo per ridurre la pressione del registro nelle architetture affamate del registro. Se hai abbastanza registri, basta modificarne uno e ignorare il risultato. Il trucco di avere un registro 0 con valore di input 0 è solo un trucco di codifica conveniente quando si hanno abbastanza registri che fissarne uno su 0 è meglio che aumentare il numero di istruzioni. È quindi conveniente usarlo anche come target (riduce il numero di false dipendenze).

  2. Codifica di nuovo. Se codifichi la condizione in salti, avrai salti con 3 operandi (i due da confrontare e il target del salto), due dei quali ti piacerebbe essere valori immediati, uno che vorresti essere grande quanto possibile (i salti hanno spesso il proprio formato di codifica in modo che il target possa utilizzare il maggior numero di bit possibile). Oppure lasci cadere le possibilità.

  3. L'uso delle bandiere ti dà maggiori opportunità di impostarle. Non sono solo le operazioni di confronto che possono impostare le bandiere, ma qualunque cosa tu voglia. (Con l'avvertenza che più operazioni hai quali set di flag, più devi essere attento a garantire che l'ultima operazione che imposta i flag sia quella che desideri). Se si hanno flag, si è in grado di testare il numero di condizioni (spesso 16) moltiplicato per il numero di istruzioni che sono in grado di impostare i flag (se non si utilizzano flag, si finisce con circa tanti salti condizionali quanti hai cose da testare o ci sono cose che non ti permettono di testare facilmente (carry o overflow per esempio).

2 Dal punto di vista dell'attuatore

  1. Testare i flag è facile e può essere fatto rapidamente. Più complesso è il test, maggiore sarà l'effetto sul tempo di ciclo (o sulla struttura della pipeline se si esegue il pipeline). Ciò è particolarmente vero per le implementazioni più semplici, quando si arriva a un processore di fascia alta usando tutti i trucchi del libro, l'effetto è piuttosto minimo.

  2. Avere flag significa che molte istruzioni hanno più risultati (il risultato naturale e ciascuno dei flag modificati). E da un POV di microarchitettura, i risultati multipli sono negativi (devi tenere traccia della loro associazione). Quando hai solo un set di flag, che introducono dipendenze (non necessarie se il flag non viene quindi utilizzato) devi gestire un modo o l'altro. Ancora una volta, questo è particolarmente vero per le implementazioni più semplici, quando si arriva a un processore di fascia alta usando tutti i trucchi del libro, le difficoltà aggiuntive vengono sminuite dal resto del processore.


2

Su una macchina a 32 bit, un'istruzione "add-with-carry" utilizzata come parte di una sequenza di addizione multi-precisione deve accettare operandi per un valore di 65 bit e calcolare una somma di 33 bit. Le specifiche del registro di origine identificheranno la provenienza di 64 bit dell'operando e la specifica del registro di destinazione indicherà dove dovrebbero andare i 32 bit inferiori del risultato, ma cosa fare con l'operando "aggiungi un extra" o il bit superiore del risultato? Avere il permesso di specificare come parte dell'istruzione da dove dovrebbe provenire l'operando extra e dove dovrebbe andare il bit di risultato extra sarebbe moderatamente utile, ma in genere non sarebbe così utile da giustificare un campo extra nel codice operativo. Avere una "posizione" fissa per gestire il flag carry può essere un po 'imbarazzante dal punto di vista della pianificazione delle istruzioni, ma

Se si cercasse di progettare un set di istruzioni per consentire l'aritmetica multi-precisione ma ogni istruzione fosse limitata a due operandi a 32 bit e un operando di destinazione a 32 bit, si potrebbe implementare un "add" a 64 bit in quattro istruzioni: "set da r5 a 1 se r0 + r2 porterebbe o zero altrimenti; calcolare r4 = r1 + r3; calcolare r5 = r4 + r5; calcolare r4 = r0 + r2 ", ma andare oltre richiederebbe tre istruzioni per ogni parola aggiuntiva. La disponibilità di una bandiera carry come fonte e destinazione supplementare riduce il costo di un'istruzione per parola.

Si noti, tra l'altro, che avere un bit di istruzione controlla se l'istruzione aggiorna il registro flag può facilitare l'esecuzione fuori servizio, poiché le istruzioni che usano o modificano i bit di bandiera devono mantenere la loro sequenza l'una rispetto all'altra, ma istruzioni che non possono essere riorganizzato liberamente. Data la sequenza:

ldr  r0,[r1]
add  r0,r0,r2
eors r4,r5,r6

un'unità di esecuzione potrebbe riconoscere abbastanza facilmente che la terza istruzione potrebbe essere eseguita senza dover attendere la lettura dei dati [r1], ma se la seconda istruzione fosse stata adds r0,r0,r2possibile sarebbe possibile solo se l'unità di esecuzione fosse in grado di garantire che al momento in cui qualcosa avesse tentato di utilizzare i flag, il flag zero manterrà il valore stabilito nella terza istruzione ma il flag carry manterrà il valore nella seconda.


1
"bit di istruzione controlla se l'istruzione aggiorna il registro flag": disponibile ad esempio in PowerPC, SPARC.
TEMLIB

MIPS utilizza "r5 = r1 + r2; impostare r6 se r6 è inferiore a r1; r7 = r3 + r4; r5 = R5 + R6;". Alcune estensioni SIMD potrebbero utilizzare confronti che impostano tutti i bit su zero o su uno (ovvero zero o -1 doppio complemento intero) per trovare il carry e la sottrazione per applicare il carry.
Paul A. Clayton,

@ PaulA.Clayton: penso che volevi dire "se r5 è inferiore a r1". In che modo i MIPS gestiranno la matematica più a lungo? Richiederebbe tre, più di tre o meno di tre istruzioni per parola?
supercat

@supercat Sì, avrebbe dovuto essere "impostato r6 se r5 è inferiore a r1"!
Paul A. Clayton,

@ PaulA.Clayton: come si potrebbe aggiungere, ad esempio, due numeri di 64 parole (2048 bit) su un MIPS a 32 bit? Esiste un modo efficace per gestire i carry in e out delle fasi intermedie?
supercat

0

Risposta semplice ... veloce operazione di memoria economica che non richiede assolutamente l'utilizzo interno del bus, tranne le istruzioni stesse. Può essere usato come bool di stack senza uno stack o un bit di processo, senza memoria.


1
Questa risposta è piuttosto leggera nei dettagli. Non sono necessarie risposte lunghe, ma qualcosa di più corposo sarebbe un netto miglioramento.
David Richerby,

impostare un flag o confrontare un valore di flag è una singola istruzione senza altre informazioni sotto forma di argomenti che verrebbero inclusi nel codice assembly. i flag sono anche il risultato del funzionamento o del test dell'uprocessore e possono essere utilizzati in modo efficiente per il branch. sono il bit effettivo che viene attivato o impostato quando due valori vengono confrontati nei registri.
SkipBerne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.