di  -  mercoledì 2 giugno 2010

Dopo una serie di articoli sull’Amiga faccio un breve salto indietro agli ARM, poiché avevo tralasciato un argomento per chiudere (temporaneamente, in attesa che la casa inglese tiri fuori qualche altro gioiello dal cilindro) il filone dedicato a queste architetture.

Sappiamo che, oltre all’architettura “base”, cioè all’ISA a 32 bit, che rappresenta la più generale e performante incarnazione, col tempo sono arrivate delle nuove ISA a 16 bit (e non solo) per aumentare la densità del codice. Parlo di Thumb e del suo successore, Thumb-2 (quest’ultimo ha aggiunto una vagonata di istruzioni a 32 bit).

ARM s’è anche cimentata con l’accelerazione Java, aggiungendo una nuova ISA (e modalità di esecuzione), Jazelle, che riprende buona parte degli opcode della Java Virtual Machine (JVM) eseguendoli in hardware.

Se consideriamo che il core di questi microprocessori è estremamente ridotto (circa 35 mila transistor per la versione 6 dell’architettura, e 50 mila per la versione 7 che troviamo nei Cortex-M0), i 12 mila transistor di Jazelle sono un numero decisamente elevato e che complica, tra l’altro, il decoder (che deve eseguire tutt’altro genere di istruzioni). Inoltre serve esclusivamente per la JVM.

Probabilmente è partendo da queste considerazioni, e dall’introduzione di Thumb-2, che ARM ha pensato bene di sviluppare una nuova modalità, chiamata ThumbEE (che è basata su Thumb-2 con qualche modifica e nuova istruzione), in modo da supportare qualunque virtual machine a un costo irrisorio, ed eseguendo al contempo codice “nativo” (codice ThumbEE, al contrario di bytecode Java).

Si tratta, quindi, di un prodotto decisamente diverso, anche come target (Jazelle credo rimarrà saldamente ancorata ai dispositivi mobile che offrono J2ME, grazie anche all’esiguo consumo di memoria), poiché prevede che il bytecode della virtual machine venga traslato “al volo” (tramite un JIT compiler) in codice ThumbEE, per poi essere eseguito, similmente a quanto avviene con la JVM utilizzata sui PC.

Da una parte siamo in presenza di codice “nativo”, ma dall’altra serve innanzitutto una prima fase di compilazione del codice, che richiede necessariamente l’allocazione di memoria addizionale rispetto a quella in cui risiede il solo bytecode della virtual machine, prima dell’effettiva esecuzione.

Una volta compilato, il codice è molto simile a quello eseguito in modalità Thumb-2, dalla quale il nuovo ambiente di esecuzione differisce per pochi, ma particolari e utili (dato l’obiettivo per cui è stata sviluppata), accorgimenti che riguardano il funzionamento di alcune istruzioni, la rimozione e l’aggiunta di altre.

Nel primo caso rientrano tutte le istruzioni denominate di load / store, a cui si aggiungono quelle “implicite” di POP e PUSH (per inserire e rimuovere elementi nel/dallo stack) e quelle di salto “tabellare” TBB e TBH (il salto avviene usando una tabella di byte o half-word, che verrà aggiunto al Program Counter).

La modifica consiste nel fatto che il registro utilizzato come “base” per la formazione dell’indirizzo viene controllato per verificare se risulta essere zero oppure no; nel primo caso l’esecuzione passa a un opportuno handler, mentre nel secondo si procede normalmente col calcolo dell’indirizzo, il load (o lo store) del dato, e il regolare completamento dell’esecuzione dell’istruzione.

Il vantaggio di questa semplice, ma geniale, soluzione è ovvio: in questo modo si intercettano le famigerate NullPointerException tanto care ai programmatori Java, dovute al deferenziamento di un puntatore nullo.

Personalmente, però, terrei sempre abilitata questa caratteristica anche con gli altri linguaggi; ad esempio con C / C++ potrebbe limitare notevolmente i famigerati segmentation fault o, peggio ancora, i bug intermittenti dovuti all’accesso (specialmente in scrittura) a zone di memoria “improprie”.

Per far fronte all’introduzione di altre istruzioni è stato necessario sacrificarne due disponibili in modalità Thumb, LDM e STM, per liberare un intero “slot” da 12 bit utilizzabile liberamente per la codifica dei nuovi arrivi:

In realtà è ancora possibile eseguire il load o lo store di più registri allo stesso tempo, sfruttando le apposite istruzioni ARM (che tra l’altro sono anche più flessibili), e quindi passando alla codifica a 32 bit introdotta con Thumb-2 allo scopo. Si tratta, in buona sostanza, di un raddoppio dello spazio (32 bit anziché 16) per utilizzare la medesima funzionalità, il che rappresenta un buon compromesso considerato l’obiettivo che ARM aveva bisogno di raggiungere.

Sempre sul campo dei “controlli” a runtime, è stata aggiunta una nuova istruzione, CHKA (i fan del Motorola 68000 saranno felici, visto che ricorda molto la CHK), che controlla se un registro supera il valore (intero senza segno) di un altro e, nel caso, invoca anche qui un apposito handler. E’ scontato l’ambito di utilizzo, visto il significato dello mnemonico usato (CHecK Array).

Molte aggiunte all’ISA hanno riguardato, invece, la sezione di load / store, con ben tre istruzioni per il primo tipo e una per il secondo. Lavorando con una virtual machine in genere al bytecode in esecuzione è associata anche una lista di costanti dai cui gli opcode vanno a “pescare” i valori già noti che servono loro.

Una nuova istruzione LDR serve proprio a questo: a prelevare una word (quindi 32 bit) usando R10 come registro base e un offset da 0 a 31 word (5 bit in tutto) per generare l’indirizzo di memoria. R10 risulta, pertanto, hard coded (si può usare soltanto questo registro), e richiama alla mente la specializzazione dei registri dell’ARM quando lavora in modalità Jazelle (per maggiori informazioni vi prego di rileggervi l’articolo di cui ho fornito il link prima).

Molto più strana è un’altra LDR che funziona sempre con un offset immediato, ma a 3 bit (quindi 8 word al massimo indirizzabili), solo che quest’ultimo viene sottratto a uno dei registri da R0 a R7 specificati (quindi l’indirizzo è nella forma Rn + offset, con n = 0..7). Dal manuale sembra che sia utile per operazioni sugli array, ma avendo lavorato con la virtual machine di Python non ne vedo l’utilità; probabilmente la JVM mostra un pattern che calza a pennello e sarà il motivo che ha spinto ARM ad aggiungerla.

A chiudere l’argomento load / store ci sono un paio di istruzioni, una per ogni tipo, che riguardano l’accesso (anche in scrittura, quindi) al cosidetto frame. Si tratta di una struttura (che generalmente si trova nello stack, ma in CPython, ad esempio, vengono allocati appositi blocchi di memoria) che conserva tutti i dati relativi a una chiamata a funzione (o metodo).

Una breve spiegazione si trova qui, mentre di seguito trovate un’immagine dallo stesso link che ne mostra il funzionamento:

Come si può vedere, nello stack frame sono memorizzati l’indirizzo di ritorno (al chiamante), i parametri che sono stati passati, e le variabili locali definite. Le istruzioni LDR e STR studiate allo scopo utilizzano il registro R9 (quindi anche qui troviamo una “specializzazione”) per indirizzare lo stack frame, a cui viene sommato un offset per selezionare fino a 64 word (6 bit di valore immediato).

Le innovazioni di ThumbEE terminano con un gruppo di nuove istruzioni che riguardano gli handler, che abbiamo già incontrato per quanto riguarda il controllo sui puntatori nulli e l’istruzione CHKA, ma che meritano a questo punto una breve spiegazione prima di continuare.

Per handler (o “gestore”) s’intende una funzione di callback che viene chiamata quando si verifica una particolare condizione, come possono essere le due di cui sopra. All’invocazione la CPU provvede, pertanto, a “congelare” lo stato dell’esecuzione, conservando, eventualmente, nel registro di link (LR in Thumb, R14 nell’ARM) l’indirizzo dell’istruzione “chiamante” e saltando poi all’apposito codice di callback che provvederà a smaltire la richiesta.

Nella modalità ThumbEE esiste una tabella di puntatori che conserva gli indirizzi di tutti gli handler da utilizzare; ovviamente due sono assegnati rispettivamente per l’intercettazione di puntatori nulli, e per il controllo degli indici; gli altri sono, invece, richiamati in maniera programmatica tramite quattro istruzioni che specificano l’indice dell’handler da richiamare.

Queste quattro istruzioni sono suddivise in due gruppi, con due istruzioni per chiamate a handler con (HBP, HBLP) o senza (HB, HBL) parametro. Le versioni con la “L” provvedono a conservare nel registro di link (LR, R14) l’indirizzo dell’istruzione successiva (quindi prevedono il ritorno alla chiamata), mentre quelle senza si limitano a invocare l’handler.

Le istruzioni senza parametro prevedono l’uso di un valore immediato a 8 bit, quindi permettendo di richiamare fino a 256 handler diversi (sempre facendo riferimento alla tabella degli handler del ThumbEE). Non è difficile immaginare, pertanto, che a ogni handler possa corrispondere la gestione di un determinato bytecode.

Infine le due con parametro restringono a 5 bit l’indice dell’handler (riducendo l’accesso ai soli primi 32 della suddetta tabella), ma permettono di specificare un valore immediato a 3 bit che verrà memorizzato nel registro R8 (altra specializzazione simil-Jazelle), che sarà pertanto a disposizione dell’handler.

Personalmente avrei preferito che fossero selezionabili ulteriori 32 handler, anziché rimappare i primi 32, per una maggior flessibilità (al limite si potevano far coincidere i puntatori), ma non ho trovato nessuna informazione a riguardo; assumo in ogni caso che ci sia stata una qualche ragione tecnica dietro a questa scelta.

Con l’introduzione degli handler è possibile immaginare alcuni scenari d’uso. Un primo in cui, ad esempio, a ogni bytecode viene generato un opcode di chiamata a handler (con eventualmente a seguire alcuni parametri), quindi in esecuzione ci sarà rigorosamente una sfilza di handler eseguiti; si tratta di una soluzione estremamente semplice per implementare rapidamente una virtual machine.

Uno scenario più complesso, ma decisamente più efficace, può prevedere, invece, una compilazione in istruzioni native ThumbEE per i bytecode più comuni, relegando agli handler la gestione di quelli meno comuni e più complessi (similmente a Jazelle, che implementa soltanto gli opcode più “gettonati”).

Questa soluzione garantisce prestazioni mediamente superiori, a fronte di una maggior complessità del compilatore JIT e consumo di memoria (a causa del maggior numero di opcode generati per un determinato bytecode).

Per il resto lascio spazio all’immaginazione dei lettori. Personalmente ho trovato interessante questa nuova modalità perché permette di implementare numerose virtual machine non strettamente legate a Java.

Pensando a CPython, avrei comunque notevoli difficoltà a realizzarne un porting per ThumbEE, in quanto il codice del loop principale che esegue i bytecode è decisamente diverso e richiederebbe, di fatto, una VM nuova di zecca.

Ciò non toglie che il lavoro fatto da ARM è ammirevole, specialmente alla luce della semplicità (e, penso, al bassissimo uso di transistor rispetto a Jazelle) con cui è stata realizzata la nuova modalità ThumbEE. Come ho già detto in passato, è un altra, preziosa, freccia che mette a disposizione per questa splendida architettura.

14 Commenti »

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.

  • # 1
    TheKaneB
     scrive: 

    ottimo lavoro Cesare :-)
    Come sempre i tuoi articoli tecnici sono chiari e molto ben fatti!

    Ciao!

  • # 2
    supertigrotto
     scrive: 

    Da elettrotecnico,so appena fare qualcosa in c e c++ e ho una piccola base di elettronica,cerco di fare l’autodidatta ma purtroppo non ho nessuno che mi indirizza (ci sarebbero le università on line ma tra la cassaintegrazione,i pochi soldi che guadano e il poco tempo a disposizione non ho la possibilità di farla),però leggo sempre tutti gli articoli fino infondo,anche se per l80% non capisco niente!
    Da quel poco che capisco,devo dire che sono veramente scritti bene per chi ne capisce!
    Complimenti ad appunti digitali,tra retrogaming e spiegazioni scientifiche varie (tipo turbine pelton,kaplan etc,astrofisica e altro),è diventato un appuntamento quotidiano come hwupgrade e tom’s.
    Leggervi è sempre un grande piacere,un applauso a tutti i redattori!

  • # 3
    Cesare Di Mauro (Autore del post)
     scrive: 

    Posso dire che senza il vostro apprezzamento non sarei ancora qui a scrivere questi articoli. Perché se è vero che gli articoli devono essere ben fatti (tecnicamente parlando) e per questo ci mettiamo tutti i mezzi, è anche vero che i lettori devono pure poterli apprezzare, altrimenti ha poco senso scrivere tanto per lasciare qualche traccia sul web (che è un universo enormemente in espansione, dove un’informazione, per quanto “bella”, si può facilmente disperdere).

    Per cui mi fa piacere leggere commenti come questi, anche se mi rendo conto che, a causa dell’eccessivo livello di “tecnicità”, non possono essere “potabili” per tutti. Spero che, comunque, qualcosa “resti” e vada a far parte del proprio bagaglio culturale.

  • # 4
    Gurzo2007
     scrive: 

    come sempre ottimo articolo cesare ;)

  • # 5
    Giullo
     scrive: 

    ottimo articolo

  • # 6
    supertigrotto
     scrive: 

    Cesare,lo so che gli articoli non sono alla portata di tutti,è normale.se trovassi un professore di informatica in pensione che avesse la pazienza di spiegarmi l’informatica dalla a alla z sarebbe una cosa stupenda.
    Io comunque gli articoli li leggo per intero anche se tante cose non le capisco (del tipo come funzionano puntatori,stack,array etc),a scuola ci hanno spiegato un po’ di c e c++,poi qualcosa ho fatto come autodidatta.
    Se tu dovessi scrivere articoli per cui tutti riuscirebbero a capire,probabilmente ogni articolo sarebbe lungo come un manuale vero e proprio.
    Magari un giorno,rileggendoli,riuscirò a capirli fino in fondo e soprattutto saprei dove andare a reperirli!

  • # 7
    Nicola
     scrive: 

    Ottimo come sempre, sto cominciando ad appassionarmi a questa architettura, mi è venuta una bella idea in mente….

    Cesare potresti indicarmi delle risorse valide per “imparare” a sviluppare su ARM? libri, tutorial e quant altro.

    Grazie in anticipo

  • # 8
    Cesare Di Mauro (Autore del post)
     scrive: 

    Purtroppo non ho libri da consigliarti. Personalmente studio il reference manual di tutte le architetture che m’interessano, ma è roba estremamente tecnica, quindi “per addetti ai lavori”.

  • # 9
    Nicola
     scrive: 

    ok Cesare, grazie comunque. Userò anche io allora il vecchio metodo. Pensavo che per queste architetture “moderne” ci fosse dell’altro.

    Thanks ^^

  • # 10
    banryu
     scrive: 

    Articolo interessantissimo, unito a quello su Jazelle.
    Complimenti Cesare, per l’ottima divulgazione degli argomenti.
    Magari non capitò tutto, mancandomi le conoscenze neccessarie, però, grazie al modo in cui strutturi gli articoli, riesco a seguire il filo del discorso e ad avere scorci delle problematiche di basso livello e dei fattori che entrano in gioco e che vengono pesati nella scelta del compromesso effettuata alla luce del risultato/scopo da ottenere dell’architettura in esame.

    Ciao :)

  • # 12
    Cesare Di Mauro (Autore del post)
     scrive: 

    Beh, che fosse necessario un bel po’ di lavoro per sfruttare pienamente ThumbEE era scontato.

  • # 13
    Frafra
     scrive: 

    ThumbEE sembra più per PyPy che per CPython. Sarebbe davvero interessare vedere che tipo di migliorie a livello prestazionale potrebbe portare.

  • # 14
    Cesare Di Mauro (Autore del post)
     scrive: 

    Per com’è strutturato lo vedo meglio per CPython.

    PyPy si fonda sull’emissione di codice binario per una determinata funzione che ha superato una certa soglia riguardo al numero di chiamate che sono state fatte a suo carico.

    Il potenziale, insomma, è racchiuso nel codice generato, e qui ThumbEE può aiutare ben poco.

Scrivi un commento!

Aggiungi il commento, oppure trackback dal tuo sito.

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.