Dopo aver analizzato la distribuzione delle modalità d’indirizzamento verso la memoria, e prima di mettere in relazione gli ultimi argomenti trattati, è necessario focalizzare l’attenzione sulle statistiche relative ai valori immediati, componente di fondamentale importanza che ha un notevole peso nell’ISA e nella definizione della struttura degli opcode di un’architettura.
I valori immediati sono interi che, come sappiamo, possono essere dotati di segno oppure no. Nell’ambito di questa ricerca si è assunto, per una questione di semplicità, che fossero tutti quanti dotati di segno, a prescindere dall’istruzione che ne facesse uso.
Ciò non inficia la generalità della discussione, poiché sono pochi i casi in cui un’istruzione richieda effettivamente che un determinato valore non sia dotato di segno.
Il caso più banale è quello del caricamento di un registro con un valore pari alla sua dimensione (32 bit per x86 e 64 bit per x64): non essendo noto l’uso che se ne farà, quel valore potrebbe essere indifferentemente trattato come con segno o senza. Ciò che conta, per il processore, è che in quel registro finisca esattamente la configurazione di bit che interessava al programmatore.
Meno banale è il caso dei valori immediati utilizzati per indicare lo shift nelle istruzioni aritmetiche o logiche che ne fanno uso. Risulta evidente che il valore debba essere considerato senza segno (il processore tronca il valore immediato ai primi 5 o 6 bit, a seconda che si tratti di ISA a 32 o 64 bit), ed è proprio ciò che si riscontra nella norma (nessun valore di shift supera il massimo consentito), per cui, di fatto, è possibile trattarli tutti come valori con segno.
Più pelosa si fa la questione di altri casi particolari, come le istruzioni di IN e OUT, che richiedono obbligatoriamente un valore senza segno e possono coprire l’intero range degli 8-bit concessi. Idem per l’istruzione INT, che ricade nella medesima situazione. Trattandosi, però, di istruzioni più uniche che rare, un eventuale valore superiore a 127 non turberebbe la situazione generale, che può contare su numeri ben più elevati che, quindi, sostanzialmente annullano gli effetti di eventuali “spurie” presenti nei dati.
Fatta questa debita premessa, passiamo subito all’analisi della distribuzione dei valori immediati riscontrati per x86 facendo sempre uso della beta pubblica di Adobe Photoshop CS6 a 32 bit (PS32):
Immediates: Size Count IMM1 51771 IMM32 46677 IMM2 34134 IMM4 30042 IMM5 29738 IMM6 20726 IMM3 16635 IMM16 12470 IMM7 9213 IMM8 8765
Poiché non era certo possibile conteggiare la ricorrenza di ogni distinto valore, si è deciso di adottare come criterio di aggregazione quello dello spazio occupato in termini di bit da un determinato valore, tenendo conto che è dotato di segno e che tale segno viene utilizzato (ricopiandolo in tutti i bit superiori) per estendere il valore all’intera dimensione richiesta (8, 16, 32, o 64 bit, a seconda della dimensione su cui opera l’istruzione).
Per fare un esempio chiarificatore e prendendo il primo risultato, col termine IMM1 indichiamo il fatto che il valore immediato richieda un solo bit per essere rappresentato. Potendo disporre di un solo bit, ciò significa che lo stesso bit verrà ripetuto e ricopiato in tutti i bit superiori per formare il valore finale desiderato.
Potendo, questi, assumere soltanto due configurazioni possibili (aventi tutti i bit a 0 oppure a 1) e utilizzando la canonica rappresentazione in complemento a 2, ciò significa che con un solo bit possiamo rappresentare soltanto i valori 0 (tutti i bit a 0) e -1 (tutti i bit a 1).
Applicando lo stesso ragionamento a tutti gli altri casi (tranne a IMM32, per ovvie ragioni), si ottiene che, ad esempio, con 2 bit è possibile rappresentare i valori che vanno da -2 a 1, con 3 bit si va da -4 a 3, e così via.
Chiarito in che modo i valori immediati sono stati raggruppati, e passando alle analisi dei numeri, il quadro che ne viene fuori risulta abbastanza scontato: la stragrande maggioranza dei valori immediati richiede al più 8 bit per poter essere rappresentati (memorizzati nell’opcode), potendo coprire ben il 77% del totale, mentre il 18% richiede 32 bit (più di 16 bit), e soltanto il 5%, infine, 16 bit (più di 8 bit).
Anche l’utilizzo dei valori immediati risulta abbastanza lineare. Fatta eccezione per quelli di 3 bit, che si posizionano dopo quelli di 6, si assiste a una progressiva diminuzione della frequenza, partendo ovviamente da quelli di un solo bit, e finendo con quelli di 8 bit.
Le sorprese, invece, arrivano con x64, facendo uso della solita beta pubblica di Adobe Photoshop CS6 a 64 bit (PS64), la quale ci mostra il seguente scenario:
Immediates: Size Count IMM2 37408 IMM1 30865 IMM7 23925 IMM16 18578 IMM4 13131 IMM3 12740 IMM5 10864 IMM8 10800 IMM6 9034 IMM32 5711 IMM64 1208
Qui la linearità riscontrata in precedenza si perde quasi del tutto, con la prima posizione occupata dai valori di due soli bit, e soltanto a seguire troviamo quelli che ne fanno uso di uno solo, mentre la terza piazza è occupata da quelli di ben 7 bit, che su x86, invece, risultavano al penultimo posto.
La tendenza è certamente preservata, poiché i valori che necessitano di pochi bit sono complessivamente i più frequenti, ma ciò che prima sembrava naturale e scontato, a un livello di dettaglio più basso non lo è più.
A livello macroscopico, invece, la tendenza risulta ben più consolidata, con i valori che necessitano di massimo 8 bit che rappresentano l’85% del totale, quelli a 16 bit (più di 8 bit) che con l’11% scavalcano quelli a 32 (più di 16 bit), i quali adesso sono soltanto il 3%, e infine vediamo che sono stati ovviamente introdotti anche valori a 64 bit (più di 32 bit), che appaiono, però, abbastanza rari (circa l’1%).
Le differenze rispetto a x86 sono, dunque, notevoli, con x64 che privilegia nettamente i valori più piccoli, e ciò viene rispecchiato anche dal fatto che, dopo quelli a 8 bit, sono proprio quelli a 16 bit a essere i più frequenti, mentre in precedenza lo erano quelli a 32 bit.
Una sparuta minoranza sono, invece, quelli a 64 bit, ma anche questo era prevedibile, poiché soltanto un’istruzione dell’ISA (MOV Reg64,Imm64) è in grado di caricare valori di questa dimensione, e comunque è difficile che vengano utilizzati valori così grandi, se non per indirizzi.
Gli indirizzi e la diversa ABI utilizzata nelle due architetture sono, infatti, la risposta al crollo dei valori a 32 bit su x64, che invece su x86 erano fra i più frequenti. Lo erano perché spesso si trattava dell’indirizzo di memoria da caricare sullo stack (con l’istruzione PUSH Imm32) prima di chiamare una routine, cosa che su x64 avviene usando un registro (tramite l’istruzione LEA Reg64,[RIP+DISP32]).
Ciò è anche uno dei motivi per cui su x64 vengono utilizzati meno valori immediati (174264) rispetto a x86 (260171), ma non è sufficiente a spiegare una così netta differenza, considerato che i valori a 32 bit su x86 erano 46677 e non rappresentavano tutti dei puntatori, mentre passando da x86 a x64 ci sono circa 86 mila valori immediati in meno (in generale).
Nel prossimo articolo della serie verranno messi in relazione il numero degli argomenti e le modalità d’indirizzamento, in modo da fornire un quadro più dettagliato sulle caratteristiche delle istruzioni che incontriamo in queste due architetture.