Je transforme le code C le plus simple
#include <stdio.h>
int main()
{
return 0;
}
à son LLVM IR, en utilisant
clang -emit-llvm -S hello.c
L'IR généré est:
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
Cependant, je ne comprends pas cet IR. (La doc LLVM aide mais pas beaucoup pour les débutants)
%1 = alloca i32, align 4
? À quoi cela correspond-il dans le code d'origine?store i32 0, i32* %1
define i32 @main() #0
Ceci définit une fonction appelée main
qui retourne un entier 32 bits. Le #0
signifie utiliser les attributs nommés #0
pour la fonction. Par exemple, il peut y avoir quelque chose comme attributes #0 = { alwaysinline alignstack=4 }
dans l'IR, et ces attributs seront appliqués à main
.
%1 = alloca i32, align 4
Cela alloue un entier 32 bits sur la pile. %1
est le nom d'un pointeur vers cet emplacement sur la pile. Le align 4
garantit que l'adresse sera un multiple de 4
store i32 0, i32* %1
Cela définit l'entier 32 bits pointé par %1
à la valeur 32 bits 0. C'est comme dire *x = 1
en C++
ret i32 0
Cela revient de la fonction avec une valeur de retour 32 bits de 0
L'affectation est étrange, étant donné que vous n'avez pas de variable locale dans main
. LLVM utilise BasicBlock
pour représenter des groupes d'instructions, et un bloc de base a un point de sortie et une liste d'instructions. Je suppose que le compilateur a décidé d'utiliser le return
comme sortie du bloc de base et a choisi de mettre au moins une instruction dans le bloc. L'affectation est fondamentalement un no-op.
Le %n
sont des registres virtuels qui seront résolus en registres réels lors de la génération de code pour la machine cible.
Le i32
est là pour les informations de type. Dans le code d'origine, c'était un int
que votre compilateur considérait comme un entier 32 bits.
alloca
sert à allouer de l'espace sur la pile. Dans cet exemple, c'est i32
(Entier 32 bits) afin que vous puissiez charger le 0 pour la valeur de retour. align 4
donne à cette allocation un alignement de 4 octets, c'est-à-dire que le pointeur de pile sera sur une adresse alignée de 4 octets.
Ce n'est pas la représentation la plus efficace mais ce n'est pas le but si IR. L'IR doit être portable pour différentes architectures. Il revient ensuite au backend de produire un code machine efficace.
Manuel de référence du langage LLVM
La raison pour laquelle alloca
et store
est liée au fait que c'est la fonction main
. Si vous aviez appelé cette fonction autrement, l'IR contiendrait simplement ret
comme vous vous y attendiez. D'après l'examen de l'assemblage produit pour le principal, il semble être lié au pointeur de la base de la pile, mais je ne comprends pas vraiment pourquoi il est là. Il est temps de retirer la norme C, je pense.
Mise à jour: je ne trouve rien dans la norme C, mais il semble que clang le fasse pour chaque fonction principale. Je ne connais pas assez bien la base de code de clang pour la retrouver.
Mise à jour: Voir les commentaires avec Bill Lynch ci-dessous. Ces instuctions sont là:
pour l'éventuelle implicite
return 0
que les fonctions principales ont
Les variables sont généralement placées sur la pile dans des versions non optimisées pour des raisons de débogage. Dans les versions optimisées qui utilisent des registres réels, la valeur peut disparaître avant la fermeture de la fonction.
Le commentaire sur la portabilité n'est pas précisément correct, si cet IR était passé par 'opt' cela éliminerait le magasin de pile.