Salta al contenuto
Note per Studenti Esercizio 33 · array in assembly ARM, contare e invertire

Esercizio 33array in assembly ARM, contare e invertire

In questa pagina 4

Testo (tipo di esercizio previsto dalla modalità di verifica del corso per la programmazione in assembly ARM: "sviluppare, analizzare e comprendere semplici programmi in linguaggio assembly ARM"; nessun tema pubblico di questo tipo è stato trovato, quindi il testo è originale). Si usa ARM a 32 bit con la convenzione di chiamata AAPCS (Stack e chiamata di funzioni in ARMChiamata con BL e ritorno con BX LR; convenzione di chiamata AAPCS (argomenti in r0-r3, risultato in r0, registri da preservare r4-r11); stack discendente pieno con PUSH e POP; record di attivazione; funzioni foglia e non foglia; esempio ricorsivo del fattoriale con evoluzione dello stack.Stack e chiamata di funzioni in ARM →): argomenti in r0–r3, risultato in r0, ritorno con BX lr; le funzioni qui sotto sono foglia e usano solo r0–r3 e r12, quindi non devono salvare niente.

Tradurre in assembly ARM le seguenti funzioni C, spiegando la scelta delle istruzioni:

c
int conta(const int *v, int n, int soglia) {      // quanti elementi sono > soglia (con segno)
    int c = 0;
    for (int i = 0; i < n; i++)
        if (v[i] > soglia) c++;
    return c;
}

void inverti(int *v, int n) {                      // inverte l'array in loco
    int *p = v, *q = v + n - 1;
    while (p < q) {
        int t = *p; *p = *q; *q = t;
        p++; q--;
    }
}

Teoria: Array e strutture dati in assembly ARMArray in memoria e calcolo dell'indirizzo di un elemento; scorrimento con indice o con puntatore; esempi svolti (somma, massimo, copia); stringhe terminate da zero e calcolo della lunghezza; matrici per righe; struct con offset dei campi e allineamento.Array e strutture dati in assembly ARM →, Strutture di controllo in assembly ARMSalti B e condizionati, codici di condizione con e senza segno; traduzione di if, if-else, while, for e do-while da C ad ARM; esecuzione condizionata per eliminare salti brevi; switch con tabella di salto.Strutture di controllo in assembly ARM →, Istruzioni ARM di accesso alla memoriaLDR e STR per parole, byte e mezze parole con e senza segno; modalità di indirizzamento con offset immediato o registro scalato, pre-indicizzata con write-back e post-indicizzata, con esempi di indirizzo effettivo; caricamento di indirizzi e costanti; LDM e STM.Istruzioni ARM di accesso alla memoria →.


1. conta

Registri. All'ingresso: r0 = v (indirizzo del primo elemento), r1 = n, r2 = soglia. Si usano r3 (elemento letto) e r12 (contatore c). Si scorre l'array con il puntatore r0 e si consuma n contando alla rovescia: il test n > 0 sostituisce i < n.

Scelte.

armasm
conta:  MOV   r12, #0           @ c = 0
ciclo:  CMP   r1, #0
        BLE   fine              @ n <= 0: finito
        LDR   r3, [r0], #4      @ r3 = *p; p++
        CMP   r3, r2
        ADDGT r12, r12, #1      @ se *p > soglia: c++
        SUB   r1, r1, #1        @ n--
        B     ciclo
fine:   MOV   r0, r12           @ risultato in r0
        BX    lr

Traccia con v = {5, -2, 9, 0, 7}, soglia = 3 (stato a ogni inizio di ciclo, prima del CMP r1, #0):

passo r0 (offset da v) r1 (nn rimasto) r12 (cc) elemento letto >3>3?
inizio 0 5 0
dopo 5 4 4 1 5 sì
dopo -2 8 3 1 -2 no
dopo 9 12 2 2 9 sì
dopo 0 16 1 2 0 no
dopo 7 20 0 3 7 sì

Con n=0n = 0 si esce subito e r12 = 0 è il risultato; la funzione restituisce 3.

Con indice invece del puntatore (più vicino al C): LDR r4, [r0, r3, LSL #2] con r3 come indice i e CMP r3, r1 / BGE fine per il test; ma serve un registro in più per l'elemento, e se non basta r12 ci si deve appoggiare a un registro callee-saved (r4–r11) da salvare con PUSH e ripristinare con POP: per una funzione foglia la versione col puntatore è più economica.

2. inverti

Registri. r0 = p (parte da v), r1 prima è n e poi diventa q, r2, r3 temporanei.

Scelte.

  • ADD r1, r0, r1, LSL #2: r1 = v + 4n, cioè l'indirizzo subito dopo l'ultimo elemento (offset scalato con LSL #2); SUB r1, r1, #4 lo riporta a &v[n-1]. Per n=0n = 0 risulta q=v−4<pq = v - 4 < p e il ciclo non gira.
  • Il test p < q è un confronto tra indirizzi: si usa il confronto senza segno, BHS (higher or same): si esce se p≥qp \ge q. Con BGE (con segno) un indirizzo alto potrebbe apparire negativo.
  • Lo scambio sfrutta il post-indicizzato: STR r3, [r0], #4 scrive *q (già letto in r3) in *p e fa avanzare p; STR r2, [r1], #-4 scrive il vecchio *p (in r2) in *q e fa arretrare q. Le due LDR vengono prima delle due STR, perciò non si perde nessuno dei due valori.
armasm
inverti: ADD   r1, r0, r1, LSL #2   @ r1 = v + 4n
         SUB   r1, r1, #4           @ r1 = &v[n-1] = q
ciclo:   CMP   r0, r1
         BHS   fine                 @ p >= q (senza segno): finito
         LDR   r2, [r0]             @ r2 = *p
         LDR   r3, [r1]             @ r3 = *q
         STR   r3, [r0], #4         @ *p = vecchio *q; p++
         STR   r2, [r1], #-4        @ *q = vecchio *p; q--
         B     ciclo
fine:    BX    lr

Traccia con v={1,2,3,4,5}v = \{1,2,3,4,5\} (n=5n = 5): pp e qq puntano a 1 e 5: scambio → 5 2 3 4 1; poi 2 e 4: 5 4 3 2 1; poi p=qp = q (entrambi su 3): BHS esce. Con n=4n = 4, {1,2,3,4}\{1,2,3,4\}: 4 2 3 1, poi 4 3 2 1; poi p>qp > q: esce. Il numero di scambi è ⌊n/2⌋\lfloor n/2 \rfloor: O(n)O(n) tempo, O(1)O(1) spazio.

Verifica

Le due funzioni sono state assemblate ed eseguite in un emulatore ARM (stesso codice dei riquadri) su 500 casi casuali ciascuna, con nn da 0 a 12 e valori negativi, confrontando il risultato con quello di una funzione Python equivalente; in più si è controllato che i registri callee-saved r4–r6 restino invariati dopo la chiamata. Esiti sul caso della traccia: conta → 3; inverti su {1,2,3,4,5} → {5,4,3,2,1}; su {1,2,3,4} → {4,3,2,1}; su un array vuoto non cambia niente.

Errori comuni

Versione ripasso

Tipo di esercizio previsto dalla modalità di verifica (programmazione in assembly ARM, testo originale). ARM 32 bit, AAPCS: argomenti r0–r3, risultato r0, ritorno BX lr; funzioni foglia con solo r0–r3, r12. Teoria: Array e strutture dati in assembly ARMArray in memoria e calcolo dell'indirizzo di un elemento; scorrimento con indice o con puntatore; esempi svolti (somma, massimo, copia); stringhe terminate da zero e calcolo della lunghezza; matrici per righe; struct con offset dei campi e allineamento.Array e strutture dati in assembly ARM →, Strutture di controllo in assembly ARMSalti B e condizionati, codici di condizione con e senza segno; traduzione di if, if-else, while, for e do-while da C ad ARM; esecuzione condizionata per eliminare salti brevi; switch con tabella di salto.Strutture di controllo in assembly ARM →.

conta(v, n, soglia) (elementi >> soglia, con segno): scansione con puntatore e nn alla rovescia.

armasm
conta:  MOV   r12, #0
ciclo:  CMP   r1, #0
        BLE   fine
        LDR   r3, [r0], #4      @ r3 = *p; p++
        CMP   r3, r2
        ADDGT r12, r12, #1      @ esecuzione condizionata, confronto con segno
        SUB   r1, r1, #1
        B     ciclo
fine:   MOV   r0, r12
        BX    lr

Con {5, -2, 9, 0, 7} e soglia 3 → 3.

inverti(v, n): r1 = v + 4n - 4 (qq = ultimo elemento), p = r0; test su indirizzi senza segno (BHS); LDR di entrambi gli elementi, poi STR r3, [r0], #4 e STR r2, [r1], #-4; ⌊n/2⌋\lfloor n/2 \rfloor scambi.

armasm
inverti: ADD   r1, r0, r1, LSL #2
         SUB   r1, r1, #4
ciclo:   CMP   r0, r1
         BHS   fine
         LDR   r2, [r0]
         LDR   r3, [r1]
         STR   r3, [r0], #4
         STR   r2, [r1], #-4
         B     ciclo
fine:    BX    lr

Errori comuni: passo 1 invece di 4; GT/LT sugli indirizzi; r4–r11 non salvati; STR prima delle due LDR; -4 dimenticato; BX lr omesso.

Teoria collegata