web-dev-qa-db-fra.com

Comprendre l'IR LLVM le plus simple

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. Pourquoi avons-nous %1 = alloca i32, align 4? À quoi cela correspond-il dans le code d'origine?
  2. Même question pour store i32 0, i32* %1
  3. Est-ce que alloca signifie allocation sur la pile (au lieu de l'allocation dynamique)?
  4. Que signifie "aligner 4"?
44
zell
 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.

41
Sean

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

14
DrYap

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.

2
Colin LeMahieu