2014-12-12 15 views
23

trasformo il codice più semplice CCapire il più semplice IR LLVM

#include <stdio.h> 

int main() 
{ 
    return 0; 
} 

alla sua LLVM IR, utilizzando

clang -emit-llvm -S hello.c 

Il generato IR è:

define i32 @main() #0 { 
     %1 = alloca i32, align 4 
     store i32 0, i32* %1 
     ret i32 0 
    } 

Tuttavia, lo faccio non capisco questo IR. (LLVM doc aiuta ma non tanto per i principianti)

  1. Perché abbiamo %1 = alloca i32, align 4? A cosa corrisponde nel codice originale?
  2. Stessa domanda per store i32 0, i32* %1
  3. Alloca significa allocazione nello stack (anziché allocazione dinamica)?
  4. Cosa significa "allinea 4"?
+0

L'allineamento indica che l'operazione di memoria deve essere allineata a 4 byte. Non sono sicuro del resto. – MariusSiuram

risposta

19
define i32 @main() #0 

Questa definisce una funzione chiamata main che restituisce un intero a 32 bit. Il #0 significa utilizzare gli attributi denominati #0 per la funzione.Ad esempio, potrebbe esserci qualcosa come attributes #0 = { alwaysinline alignstack=4 } nell'IR e questi attributi verranno applicati a main.

%1 = alloca i32, align 4 

Questo alloca un numero intero a 32 bit nello stack. %1 è il nome di un puntatore a questa posizione sullo stack. Il align 4 assicura che l'indirizzo sarà un multiplo di 4

store i32 0, i32* %1 

Questo imposta il numero intero a 32 bit puntato da %1 al valore 32 bit 0. Come dire *x = 1 in C++

ret i32 0 

Questo ritorna dalla funzione con un valore di ritorno a 32 bit di 0

L'assegnazione è dispari, considerando che non si dispone di una variabile locale in main. LLVM utilizza BasicBlock per rappresentare gruppi di istruzioni e un blocco di base ha un punto di uscita e un elenco di istruzioni. La mia ipotesi sarebbe che il compilatore ha deciso di utilizzare lo return come uscita dal blocco di base e ha deciso di inserire almeno un'istruzione nel blocco. Il compito è fondamentalmente un no-op.

12

Gli %n sono registri virtuali che verranno risolti in registri effettivi durante la generazione del codice per la macchina di destinazione.

Il i32 è lì per informazioni sul tipo. Nel codice originale era uno int che il compilatore considerava un numero intero a 32 bit.

alloca è per l'allocazione dello spazio nello stack. In questo esempio è i32 (numero intero a 32 bit) in modo da poter caricare lo 0 per il valore restituito. align 4 assegna questa allocazione a 4 byte, ovvero il puntatore dello stack si troverà su un indirizzo allineato a 4 byte.

Non è la rappresentazione più efficiente ma non è lo scopo se IR. IR dovrebbe essere portatile per diverse architetture. Spetta quindi al back-end per produrre codice macchina efficiente.

LLVM Language Reference Manual

Perché alloca e store sono che c'è da fare con questo essere la funzione main. Se hai chiamato questa funzione qualcos'altro, l'IR conterrebbe semplicemente ret come previsto. Dall'esame dell'assemblaggio prodotto per il main sembra che sia correlato al puntatore di base dello stack ma non capisco appieno perché sia ​​lì. Il tempo di tirare fuori lo standard C credo.

Aggiornamento: Non ho trovato nulla nello standard C ma sembra clang lo fa per ogni funzione principale. Non conosco la base del codice clang abbastanza bene da rintracciare però.

Aggiornamento: Vedere i commenti con Bill Lynch di seguito. Questi instuctions ci sono:

per l'eventuale implicito return 0 che funzioni principali hanno

+0

Grazie. Allora perché abbiamo bisogno di% 1 qui (le mie domande # 1 e # 2)? – zell

+0

@zell: non è necessario. Se hai abilitato gli ottimizzatori (con -O3 per esempio) verrebbe rimosso. –

+0

@Bill. Grazie. Anche per un IR inefficiente, dovrebbe esserci un motivo% 1. Qual è il razionale che LLVM IR abbia bisogno di un extra% 1 (capisco perfettamente che l'ottimizzazione lo escluderà)? – zell

2

Le variabili vengono solitamente messe in pila in build non ottimizzate per motivi di debug. Nelle build ottimizzate che utilizzano i registri reali, il valore potrebbe scomparire prima che la funzione esca.

Il commento sulla portabilità non è precisamente corretto, se questa IR è stata passata attraverso "opt" eliminerebbe lo stack store.

+0

Ma non c'è una variabile in main da provare e conservare in pila. Avendo guardato un sacco di altri output clang sembra sempre di memorizzare questo 0 in pila in main. – DrYap

+0

Il valore inserito è il valore restituito. –

+0

Grazie, Colin. Informazioni fantastiche Aggiungendo '-O3' alla generazione ll nelle mie stesse caes ha reso l'IR sooo molto più facile da capire :-) –