¿Cómo se compila un programa?
Este artículo es una visión muy ligera.
Tenemos un código que queremos compilar:
~/ejemplo $ cat suma_1y1.c
// Compilame (-S llegar al ensamblador y -o para final):
// gcc -S suma_1y1.c -fdump-tree-all -O0 -Wall
// gcc -o suma_1y1.bin suma_1y1.c -fdump-tree-all -O0 -Wall
#include
int main () {
int i;
int j;
int k;
i = 1;
j = 1;
k = i + j;
printf( "%d + %d = %d\n", i, j, k );
return k;
}
Y luego lo mandamos compilar con unas opciones especiales para ver todos los pasos, sin ninguna optimización y avisándonos de los posibles problemas:
~/ejemplo $ gcc -S suma_1y1.c -fdump-tree-all -O0 -Wall
Y ahora vamos a ver qué ha hecho el compilador. El "suma_1y1.c.001t.tu" es el conjunto de funciones existentes, pero no tiene especial interés.
Veamos el "original":
~/ejemplo $ cat suma_1y1.c.003t.original
;; Function main (main)
;; enabled by -tree-original
{
int i;
int j;
int k;
int i;
int j;
int k;
i = 1;
j = 1;
k = i + j;
printf ((const char * restrict) (char *) "%d + %d = %d\n", i, j, k);
return k;
}
Luego hace el paso a código de tres direcciones:
~/ejemplo $ cat suma_1y1.c.004t.gimple
main ()
{
int D.2026;
int i;
int j;
int k;
i = 1;
j = 1;
k = i + j;
printf (&"%d + %d = %d\n"[0], i, j, k);
D.2026 = k;
return D.2026;
}
Nos saltamos algunos que no son interesantes (vcg, useless, lower, eh, cfg), y llegamos a cplxlower0 en el que vemos que ha detectado un bloque de sentencias (el "segundo"):
~/ejemplo $ cat suma_1y1.c.023t.cplxlower0
;; Function main (main)
main ()
{
int k;
int j;
int i;
int D.2026;
:
i = 1;
j = 1;
k = i + j;
printf (&"%d + %d = %d\n"[0], i, j, k);
D.2026 = k;
return D.2026;
}
Veclower y fixupcfg no tienen mucho interés pero mostraremos la salida de éste último:
~/ejemplo $ cat suma_1y1.c.026t.fixupcfg
;; Function main (main)
main ()
{
int k;
int j;
int i;
int D.2026;
# BLOCK 2
# PRED: ENTRY (fallthru)
i = 1;
j = 1;
k = i + j;
printf (&"%d + %d = %d\n"[0], i, j, k);
D.2026 = k;
return D.2026;
# SUCC: EXIT
}
Tras estos pasos el gcc realiza el siguiente código final para x86:
$ cat suma_1y1.s
.file "suma_1y1.c"
.section .rodata
.LC0:
.string "%d + %d = %d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $1, -16(%ebp)
movl $1, -12(%ebp)
movl -12(%ebp), %eax
addl -16(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, 12(%esp)
movl -12(%ebp), %eax
movl %eax, 8(%esp)
movl -16(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl -8(%ebp), %eax
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.2.3 (Gentoo 4.2.3 p1.0)"
.section .note.GNU-stack,"",@progbits
Voy a comentar un poco algo del código (comentarios con //)
.file "suma_1y1.c" // fichero que originó este código máquina
.section .rodata
.LC0:
.string "%d + %d = %d\n" // para la llamada a printf
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp // guardamos el marco de pila
movl %esp, %ebp // el nuevo marco pila es la dirección de la pila
pushl %ecx
subl $36, %esp
movl $1, -16(%ebp) // i = 1
movl $1, -12(%ebp) // j = 1
movl -12(%ebp), %eax
addl -16(%ebp), %eax // k = i + j
movl %eax, -8(%ebp)
movl -8(%ebp), %eax // el cuarto parámetro del printf: k
movl %eax, 12(%esp)
movl -12(%ebp), %eax // el tercer parámetro del printf: j
movl %eax, 8(%esp)
movl -16(%ebp), %eax // el segundo parámetro del printf: i
movl %eax, 4(%esp)
movl $.LC0, (%esp) // el primer parámetro del printf: "%d + %d = %d\n"
call printf
movl -8(%ebp), %eax
addl $36, %esp // Se desmonta la pila
popl %ecx
popl %ebp // Se restaura el marco de pila
leal -4(%ecx), %esp // Se restaura el puntero de pila
ret // retorno del main
.size main, .-main
.ident "GCC: (GNU) 4.2.3 (Gentoo 4.2.3 p1.0)"
.section .note.GNU-stack,"",@progbits
Deseas saber más, pues accede a la página del GCC de wikibooks.
Etiquetas:
compilación,
compilador,
gcc,
programación
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario