Statistiche su x86 & x64 – parte 6 (valori immediati)

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.

Press ESC to close