2016-04-24 15 views
5

Attualmente sto imparando i linguaggi di assemblaggio e di programmazione C e ho un paio di domande a riguardo.Assieme al codice C

codice C

int arith(int x, int y, int z) { 
    int t1 = x + y; 
    int t2 = z*48; 
    int t3 = t1 & 0xFFFF; 
    int t4 = t2 * t3; 
    return t4; 
} 

codice Assembly

movl 16(%ebp),%eax   z 
leal (%eax,%eax,2), %eax z*3 
sall $4,%eax    t2 = z*48 
movl 12(%ebp),%edx   y 
addl 8(%ebp),%edx   t1 = x+y 
andl $65535,%edx   t3 = t1&0xFFFF 
imull %edx,%eax    Return t4 = t2*t3 

Invece di usare Leal e poi spostando da 4 a moltiplicare z del 48, potrei semplicemente usare imull $ 48,% eax?

Inoltre, questo sta utilizzando il registro% edx più volte. Questo significa che t1 viene sovrascritto? In altre parole, sarei ancora in grado di recuperare t1 proprio prima di T4 se volessi?

+1

Sì, No (La variabile 't1' è ottimizzata), e No. Per l'ultima domanda' x + y' è calcolata ma mai salvata. _EDX_ aveva il valore 'x + y' dopo' addl 8 (% ebp),% edx' ma l'istruzione 'andl $ 65535,% edx' lo distrugge. Se hai spostato _EDX_ in un registro come _ECX_ dopo 'addl 8 (% ebp),% edx', avresti comunque accesso alla parte x + y del calcolo. –

+0

Freddo. Il codice C sarebbe davvero tradotto dietro le quinte in modo tale che non memorizzasse ogni variabile nel proprio registro? – Dylan

+2

Non se non ce n'è bisogno. Questa è la potenza di un compilatore ottimizzante. – usr2564301

risposta

2

Cercare di far corrispondere l'assembly al codice riga per riga probabilmente non è il modo migliore per avvicinarsi a questo. Il compilatore apporta diverse ottimizzazioni per far funzionare il programma nel modo più efficiente possibile, motivo per cui potresti notare alcune incongruenze tra il tuo codice.

Per rispondere alla prima domanda, tecnicamente funzionerebbe, ma ancora una volta il compilatore esegue diverse ottimizzazioni. Quindi, mentre può sembrare più intuitivo usare imul, il compilatore ha determinato che leal e sall sono più efficienti. MODIFICA: Voglio solo sottolineare che gli operatori di spostamento dei bit sono quasi sempre utilizzati al posto di imul, quando possibile. Lo spostamento dei bit è molto più economico per la CPU poiché letteralmente sta semplicemente spostando i valori dei bit anziché tentare di eseguire alcune operazioni matematiche che potrebbero richiedere più tempo della CPU.

Ora per "sovrascrittura" t1. L'assembly non ha informazioni sulle variabili del programma: tutto ciò che sa è che è necessario eseguire alcune operazioni su alcuni valori. Sebbene l'assembly possa potenzialmente utilizzare 4 diversi registri per memorizzare t1-4, il compilatore ha determinato che non era necessario e che sono necessari solo 2 registri per tutti i valori. Se ci pensi, questo dovrebbe avere senso. La tua funzione potrebbe essere ridotta a poche righe di codice. Ovviamente non è una buona idea poiché ciò renderebbe impossibile la lettura, ma l'assemblaggio non è necessariamente progettato per essere "leggibile". Se tornassi al tuo programma ed eseguivi qualche altra operazione con t1 prima di restituire t4, potresti notare che il tuo assembly è diverso da prima e che potrebbe usare un altro registro, a seconda di come viene usato il valore.

Se si desidera realmente una versione barebone del programma in assembly, compilare con il flag -Og per disattivare le ottimizzazioni del compilatore. Potrebbe non corrispondere esattamente al tuo codice, ma potrebbe renderti più facile capire cosa sta succedendo.

+1

Grazie. Tutto ciò che hai detto aveva senso per me. È fantastico come si ottimizzi dietro le quinte del genere. – Dylan

+1

Una buona fonte di informazioni sull'ottimizzazione dell'istruzione può essere trovata in questo [documento Agner Fog] (http: //www.agner.org/optimize/instruction_tables.pdf). A seconda dell'architettura, LEAL potrebbe non raggiungere nemmeno l'ALU. Su alcune architetture x86 è fatto come parte della AGU. –

Problemi correlati