syntax llvm

syntax - Entendiendo el LLVM IR más simple



(3)

Las variables generalmente se colocan en la pila en construcciones no optimizadas por razones de depuración. En construcciones optimizadas que usan registros reales, el valor puede desaparecer antes de que la función salga.

El comentario sobre la portabilidad no es precisamente correcto, si este IR se pasa a través de ''opt'' eliminaría el almacén de pila.

Transformo el código C más simple.

#include <stdio.h> int main() { return 0; }

a su LLVM IR, utilizando

clang -emit-llvm -S hello.c

El IR generado es:

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

Sin embargo, no entiendo este IR. (LLVM doc ayuda pero no tanto para principiantes)

  1. ¿Por qué tenemos %1 = alloca i32, align 4 ? ¿A qué corresponde en el código original?
  2. La misma pregunta para la store i32 0, i32* %1
  3. ¿Asignación media de alloca en la pila (en lugar de la asignación dinámica)?
  4. ¿Qué significa ''alinear 4''?

Los %n son registros virtuales que se resolverán en registros reales al generar código para la máquina de destino.

El i32 está ahí para información de tipo. En el código original era un int que su compilador tomó como un entero de 32 bits.

alloca es para asignar espacio en la pila. En este ejemplo, es i32 (entero de 32 bits), por lo que puede cargar en el 0 para el valor de retorno. align 4 proporciona esta asignación alineación de 4 bytes, es decir, el puntero de pila estará en una dirección alineada de 4 bytes.

No es la representación más eficiente, pero ese no es el objetivo de IR. IR debe ser portátil a diferentes arquitecturas. Luego, se debe al backend producir un código de máquina eficiente.

Manual de referencia del lenguaje LLVM

¿Por qué alloca y store hay que ver con esta es la función main . Si hubiera llamado a esta función otra cosa, el IR solo contendría ret como esperaba. Al examinar el ensamblaje producido para main, parece estar relacionado con el puntero de la base de pila, pero no entiendo completamente por qué está allí. Es hora de sacar el estándar C, creo.

Actualización: No pude encontrar nada en el estándar C, pero parece que Clang hace esto para cada función principal. Sin embargo, no conozco la base del código de clang lo suficiente como para rastrearlo.

Actualización: Ver comentarios con Bill Lynch a continuación. Estas instrucciones están ahí:

Para el posible return 0 implícito return 0 que tienen las funciones principales.


define i32 @main() #0

Esto define una función llamada main que devuelve un entero de 32 bits. El #0 significa usar los atributos llamados #0 para la función. Por ejemplo, puede haber algo así como los attributes #0 = { alwaysinline alignstack=4 } en el IR, y estos atributos se aplicarán a main .

%1 = alloca i32, align 4

Esto asigna un entero de 32 bits en la pila. %1 es el nombre de un puntero a esta ubicación en la pila. El align 4 asegura que la dirección sea un múltiplo de 4.

store i32 0, i32* %1

Esto establece el entero de 32 bits al que apunta %1 al valor de 32 bits 0. Es como decir *x = 1 en C ++

ret i32 0

Esto regresa de la función con un valor de retorno de 32 bits de 0

La asignación es impar, considerando que no tienes una variable local en main . LLVM usa BasicBlock para representar grupos de instrucciones, y un bloque básico tiene un punto de salida y una lista de instrucciones. Supongo que el compilador ha decidido utilizar la return como la salida del bloque básico y ha optado por incluir al menos una instrucción en el bloque. La asignación es básicamente un no-op.