Domanda

È passato un po 'di tempo dall'ultimo assemblaggio del braccio codificato e sono un po' arrugginito sui dettagli. Se chiamo una funzione C da arm, devo solo preoccuparmi di salvare r0-r3 e lr, giusto?

Se la funzione C utilizza altri registri, è responsabile del salvataggio di quelli in pila e del loro ripristino? In altre parole, il compilatore genererebbe il codice per fare questo per le funzioni C.

Ad esempio se uso r10 in una funzione assembler, non devo spingere il suo valore nello stack, o in memoria, e pop / ripristinarlo dopo una chiamata C, vero?

Questo è per arm-eabi-gcc 4.3.0.

È stato utile?

Soluzione

Dipende dalla ABI per la piattaforma per la quale stai compilando. Su Linux, ci sono due ABI ARM; quello vecchio e quello nuovo. AFAIK, il nuovo (EABI) è in realtà AAPCS di ARM. Le definizioni EABI complete attualmente vivono qui su ARM infocenter .

Da l'AAPCS, §5.1.1 :

  • r0-r3 sono i registri argomento e scratch; r0-r1 sono anche i registri dei risultati
  • r4-r8 sono registri di salvataggio chiamate
  • r9 potrebbe essere un registro di salvataggio chiamate o meno (su alcune varianti di AAPCS è un registro speciale)
  • r10-r11 sono registri di salvataggio chiamate
  • r12-r15 sono registri speciali

Il chiamante deve salvare un registro di salvataggio della chiamata (a differenza di un registro di salvataggio del chiamante, in cui il chiamante salva il registro); quindi, se questa è l'ABI che stai usando, non devi salvare r10 prima di chiamare un'altra funzione (l'altra funzione è responsabile del salvataggio).

Modifica: il compilatore che stai utilizzando non fa alcuna differenza; gcc in particolare può essere configurato per diversi ABI diversi e può anche essere modificato sulla riga di comando. Guardare il codice del prologo / epilogo che genera non è così utile, poiché è su misura per ogni funzione e il compilatore può usare altri modi per salvare un registro (ad esempio salvandolo nel mezzo di un la funzione).

Altri suggerimenti

Per aggiungere informazioni mancanti sui registri NEON:

Da l'AAPCS , & # 167; 5.1.1 Registri di base:

  • r0-r3 sono i registri argomento e scratch; r0-r1 sono anche i registri dei risultati
  • r4-r8 sono registri di salvataggio chiamate
  • r9 potrebbe essere un registro di salvataggio chiamate o meno (su alcune varianti di AAPCS è un registro speciale)
  • r10-r11 sono registri di salvataggio chiamate
  • r12-r15 sono registri speciali

Dall'AAPCS, & # 167; 5.1.2.1 Convenzioni sull'utilizzo del registro VFP:

  • s16 & # 8211; s31 (d8 & # 8211; d15, q4 & # 8211; q7) devono essere conservati
  • s0 & # 8211; s15 (d0 & # 8211; d7, q0 & # 8211; q3) e d16 & # 8211; d31 (q8 & # 8211; q15) non devono essere preservati

Messaggio originale:
arm-to-c-calling-convention-neon- registri da salvare

Per ARM a 64 bit, A64 (dalla procedura Call Standard per l'architettura ARM a 64 bit)

Esistono trentuno registri a 64 bit per tutti gli usi (interi) visibili al set di istruzioni A64; questi sono etichettati r0-r30 . In un contesto a 64 bit, a questi registri viene normalmente fatto riferimento utilizzando i nomi x0-x30 ; in un contesto a 32 bit i registri vengono specificati utilizzando w0-w30 . Inoltre, è possibile utilizzare un registro puntatore stack, SP , con un numero limitato di istruzioni.

  • SP il puntatore dello stack
  • r30 LR The Link Register
  • r29 FP The Frame Pointer
  • r19 ... r28 Registri salvati dalla chiamata
  • r18 Il registro della piattaforma, se necessario; altrimenti un registro temporaneo.
  • r17 IP1 Il secondo registro temporaneo di chiamata all'interno della procedura (può essere utilizzato per impiallacciature di chiamata e codice PLT); altre volte può essere usato come a registro temporaneo.
  • r16 IP0 Il primo registro scratch interno alla procedura (che può essere utilizzato dalla chiamata faccette e codice PLT); altre volte può essere usato come a registro temporaneo.
  • r9 ... r15 Registri temporanei
  • r8 Registro posizione risultati indiretti
  • r0… r7 Registri parametri / risultati

I primi otto registri, r0-r7 , vengono utilizzati per passare i valori degli argomenti in una subroutine e per restituire i valori dei risultati da una funzione. Possono anche essere usati per contenere valori intermedi all'interno di una routine (ma, in generale, solo tra chiamate di subroutine).

I registri r16 (IP0) e r17 (IP1) possono essere utilizzati da un linker come registro scratch tra una routine e qualsiasi subroutine che chiama. Possono anche essere utilizzati all'interno di una routine per contenere valori intermedi tra chiamate di subroutine.

Il ruolo del registro r18 è specifico della piattaforma. Se una piattaforma ABI ha bisogno di un registro per scopi generici dedicato per trasportare lo stato interprocedurale (ad esempio il contesto del thread), dovrebbe utilizzare questo registro a tale scopo. Se la piattaforma ABI non ha tali requisiti, dovrebbe utilizzare r18 come registro temporaneo aggiuntivo. La specifica ABI della piattaforma deve documentare l'utilizzo di questo registro.

SIMD

L'architettura ARM a 64 bit ha anche altri trentadue registri, v0-v31 , che possono essere utilizzati dalle operazioni SIMD e Floating-Point. Il nome preciso del registro cambierà indicando le dimensioni dell'accesso.

Nota: A differenza di AArch32, in AArch64 le visualizzazioni a 128 e 64 bit di un registro SIMD e Floating Point non si sovrappongono a più registri in una vista più ristretta, quindi q1 , d1 e s1 si riferiscono tutti alla stessa voce nella banca dei registri.

I primi otto registri, v0-v7 , vengono utilizzati per passare i valori degli argomenti in una subroutine e per restituire i valori dei risultati da una funzione. Possono anche essere usati per contenere valori intermedi all'interno di una routine (ma, in generale, solo tra chiamate di subroutine).

I registri v8-v15 devono essere conservati da una chiamata attraverso chiamate di subroutine; i registri rimanenti ( v0-v7, v16-v31 ) non devono essere conservati (o devono essere conservati dal chiamante). Inoltre, è necessario conservare solo i 64 bit inferiori di ciascun valore archiviato in v8-v15 ; è responsabilità del chiamante preservare valori maggiori.

Le risposte di CesarB e Pavel hanno fornito citazioni da AAPCS, ma permangono problemi aperti. La chiamata salva r9? Che mi dici di r12? Che mi dici di r14? Inoltre, le risposte erano molto generali e non specifiche per la toolchain arm-eabi come richiesto. Ecco un approccio pratico per scoprire quali registri vengono salvati e quali non lo sono.

Il seguente codice C contiene un blocco assembly incorporato, che afferma di modificare i registri r0-r12 e r14. Il compilatore genererà il codice per salvare i registri richiesti dall'ABI.

void foo() {
  asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}

Utilizza la riga di comando arm-eabi-gcc-4.7 -O2 -S -o - foo.c e aggiungi gli switch per la tua piattaforma (come -mcpu = arm7tdmi per esempio). Il comando stamperà il codice assembly generato su STDOUT. Potrebbe assomigliare a questo:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    bx  lr

Si noti che il codice generato dal compilatore salva e ripristina r4-r11. Il compilatore non salva r0-r3, r12. Il fatto che ripristini r14 (alias lr) è puramente accidentale, poiché so per esperienza che il codice di uscita può anche caricare l'rr salvato in r0 e quindi eseguire un "bx r0". invece di "bx lr". O aggiungendo il -mcpu = arm7tdmi -mno-thumb-interwork o usando -mcpu = cortex-m4 -mthumb otteniamo un codice assembly leggermente diverso che assomiglia a questo:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}

Ancora una volta, r4-r11 vengono salvati e ripristinati. Ma r14 (alias lr) non viene ripristinato.

Per riassumere:

  • r0-r3 sono non salvati dalla chiamata
  • r4-r11 vengono salvati dalla chiamata
  • r12 (alias ip) è non salvato dalla chiamata
  • r13 (alias sp) viene salvato dalla chiamata
  • r14 (alias lr) è non salvato dalla chiamata
  • r15 (alias pc) è il contatore del programma ed è impostato sul valore di lr prima della chiamata della funzione

Questo vale almeno per i default di arm-eabi-gcc. Esistono opzioni della riga di comando (in particolare l'opzione -mabi) che possono influenzare i risultati.

Esiste anche una differenza almeno nell'architettura Cortex M3 per la chiamata di funzione e l'interruzione.

Se si verifica un Interrupt, spingerà automaticamente R0-R3, R12, LR, PC sullo Stack e quando tornerà dal modulo IRQ POP automatico. Se si utilizzano altri registri nella routine IRQ, è necessario spingerli / pop nello Stack manualmente.

Non credo che questo PUSH e POP automatici siano fatti per una chiamata di funzione (istruzioni di salto). Se la convenzione dice che R0-R3 può essere usato solo come argomento, registri di risultati o scratch, quindi non è necessario memorizzarli prima della chiamata della funzione perché non dovrebbe essere usato alcun valore dopo il ritorno della funzione. Ma come in un interrupt devi memorizzare tutti gli altri registri della CPU se li usi nella tua funzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top