Feeds de las etiquetas frecuentes

¿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 printfEnlace 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.

No hay comentarios: