Para este reporte decidí realizar una rutina algo común, es un factorial recursivo. Lo siguiente es el código en C:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
int factorialRec(int n) { | |
int factorial; | |
if(n <= 1) { | |
factorial = 1; | |
} | |
else { | |
factorial = n * factorialRec(n-1); | |
} | |
return factorial; | |
} | |
int main(int argc, char **argv) { //recibo un argumento | |
if(argc < 2 || argc > 2) { // verifico si la cantidad de argumentos es valida | |
printf("BAD ARGUMENTS, expected 2\n"); // mensaje si no hay suficientes argumentos | |
} | |
else { | |
int n = atoi(argv[1]); // asigno el argumento recibido a una variable | |
int resultado = 0; // variable para almacenar el resultado, se inicia en 0 | |
resultado = factorialRec(n); // el resultado es lo que regrese la funcion factorialRec. | |
printf("%d\n", resultado); // imprimimos el resultado | |
} | |
return 0; // fin | |
} |
Después de eso, hay que compilarlo para obtener el código en Ensamblador, esto con la instrucción:
gcc -S factorial.cEste es el resultado:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.file "factorial.c" ; Nombre del archivo | |
.text ; Inician las instrucciones | |
.globl factorialRec ; factorialRec es global | |
.type factorialRec, @function ; factorialRec es una funcion | |
factorialRec: ; etiqueta de la funcion factorialRec | |
.LFB0: ; etiqueta sel segmento de codigo | |
pushl %ebp ; prologo: respaldamos %ebp anterior | |
movl %esp, %ebp ; %esp apunta a %ebp | |
subl $40, %esp ; reservamos 40 bytes | |
cmpl $1, 8(%ebp) ; comparamos el primer parametro n <= 1 | |
jg .L2 ; si es mayor, saltamos a .L2 | |
movl $1, -12(%ebp) ; si no, asignamos 1 al registro %ebp-12 (factorial = 1) | |
jmp .L3 ; saltamos a .L3 | |
.L2: | |
movl 8(%ebp), %eax ; movemos el primer parametro de la funcion a %eax | |
subl $1, %eax ; y restamos 1 (n-1) | |
movl %eax, (%esp) ; movemos la direccion de memoria %eax al tope de la pila (nuevo parametro de la funcion factorialRec) | |
call factorialRec ; llamamos a factorialRec (comienza la recursion) | |
imull 8(%ebp), %eax ; multiplicamos el valor de retorno de la funcion factorialRec por 'n' | |
movl %eax, -12(%ebp) ; cargar %eax en en el registro %ebp+12 (factorial) | |
.L3: | |
movl -12(%ebp), %eax ; cargamos el valor del registro %ebp+12 en %eax (valor de retorno) | |
leave ; epilogo | |
ret ; regresamos el control a la funcion padre | |
.LFE0: ; etiqueta del segmento de codigo | |
.size factorialRec, .-factorialRec ; | |
.section .rodata | |
.LC0: ; etiqueta del segmento codigo | |
.string "BAD ARGUMENTS, expected 2" ; cadena de caracteres que contiene dicha etiqueta | |
.LC1: ; etiqueta de segmento de codigo | |
.string "%d\n" ; cadena de caracteres que contiene dicha etiqueta | |
.text ; Inician las instrucciones | |
.globl main ; 'main' es global | |
.type main, @function ; 'main' es una funcion | |
main: ; etiqueta de la funcion 'main' (principio) | |
.LFB1: ; etiqueta del segmento de codigo | |
pushl %ebp ; prologo: respaldamos %ebp anterior | |
movl %esp, %ebp ; %esp apunta a %ebp | |
andl $-16, %esp ; alineamos la pila | |
subl $32, %esp ; reservamos 32bytes | |
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la funcion | |
jle .L5 ; y si es menor o igual a 1, saltamos a .L5 | |
cmpl $2, 8(%ebp) ; comparamos el primer parametro de la funcion | |
jle .L6 ; y si es menor o igual a 2, saltamos a .L6 | |
.L5: ; etiqueta del segmento de codigo | |
movl $.LC0, (%esp) ; movemos la cadena de .L0 al tope de la pila | |
call puts ; llamamos a puts, imprime la cadena | |
jmp .L7 ; saltamos a .L7 | |
.L6: ; etiqueta del segmento de codigo | |
movl 12(%ebp), %eax ; movemos el valor del segundo parametro de la funcion %ebp+12 (argv[1]) al registro temporal %eax | |
addl $4, %eax ; le agregamos 4bytes a %eax | |
movl (%eax), %eax ; mover el valor al cual apunta %eax a la direccion del registro %eax | |
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp (tope de pila) | |
call atoi ; llamamos a 'atoi' | |
movl %eax, 24(%esp) ; asignamos el valor de %eax a la variable %esp+24 ('n') | |
movl $0, 28(%esp) ; asignamos 0 a la variable %esp+28 ('resultado') | |
movl 24(%esp), %eax ; la variable %esp+24 la asignamos al registro %eax | |
movl %eax, (%esp) ; movemos ese valor al tope de la pila (parametro de la funcion factorialRec) | |
call factorialRec ; llamamos a la funcion factorialRec | |
movl %eax, 28(%esp) ; asigamos lo que regrese la funcion (%eax) a resultado (resultado = factorialRec(n)) | |
movl $.LC1, %eax ; movemos la cadena almacenada en .LC1 a %eax | |
movl 28(%esp), %edx ; movemos el valor de %esp+28 a %edx que es un registro de entrada y salida | |
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4 | |
movl %eax, (%esp) ; movemos la direccion de %eax al valor que apunta %esp (tope de la pila) | |
call printf ; llamamos a la funcion 'printf' | |
.L7: ; etiqueta del segmento de codigo | |
movl $0, %eax ; asignamos el valor de 0 a %eax (valor de retorno) | |
leave ; epilogo del codigo | |
ret ; regresamos el control a la funcion padre | |
.LFE1: ; etiqueta del segmento de codigo | |
.size main, .-main ; las lineas son datos de la funcion padre, es decir, el sistema operativo | |
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1" | |
.section .note.GNU-stack,"",@progbits |
Después, lo siguiente fue optimizar el código, eliminando aquellas instrucciones que no fueran necesarias, la mayoría de las lineas que elimine fueron etiquetas debug y algunos movimientos de memoria innecesarios.
Cuando recién compilamos aparecerán muchas líneas basura, la mayoría comienzan con .cfi, todas esas líneas podemos eliminarlas.
Algunas etiquetas como .file, .type, tambien podemos suprimirlas
También elimine algunos intercambios de registros innecesarios, por ejemplo, el return de la función factorialRec realizaba los siguientes movimientos:
mov %eax, 12(%esp) mov 12(%esp), %eaxEste procedimiento lo elimine porque no tenia sentido hacer ese intercambio.
El nuevo código en Ensamblador es el siguiente:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.globl factorialRec ; factorialRec es una funcion global | |
factorialRec: ; etiqueta del segmento de codigo de la funcion factorialRec | |
pushl %ebp ; prologo: respaldamos el %ebp anterior | |
movl %esp, %ebp ; %esp apunta a %ebp | |
subl $40, %esp ; reservamos 40bytes | |
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la funcion | |
jg .L2 ; y si es mayor a 1, saltamos a .L2 | |
movl $1, -12(%ebp) ; asignamos 1 al registro %ebp-12 (factorial = 1) | |
jmp .L4 ; saltamos a .L4 | |
.L2: ; etiqueta del segmento de codigo | |
movl 8(%ebp), %eax ; el primer parametro de la funcion lo asignamos al registro %eax | |
subl $1, %eax ; le restamos 1 al valor del registro %eax (n-1) | |
movl %eax, (%esp) ; asignamos la direccion de memoria del registro %eax al valor en el tope de la pila (enviar un parametro) | |
call factorialRec ; llamada a la funcion factorialRec | |
imull 8(%ebp), %eax ; muliplicamos el primer parametro de la funcion por lo que hay en %eax. | |
movl %eax, -12(%ebp) ; asignamos ese valor a una variable en el registro &ebp-12 (factorial = n * factorialRec(n-1)) | |
.L4: ; etiqueta del segmento de codigo | |
leave ; epilogo de la funcion | |
ret ; regresamos el control a la funcion principal | |
.globl main ; main es una funcion global | |
main: ; etiqueta de la funcion main | |
pushl %ebp ; prologo: respaldamos el %ebp anterior | |
movl %esp, %ebp ; %esp apunta a %ebp | |
andl $-16, %esp ; alineamos la pila | |
subl $32, %esp ; reservamos 32bytes | |
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la pila (entero con la cantidad de parametros) | |
jle .L6 ; y si es menor o igual a 1, saltamos a .L6 | |
cmpl $2, 8(%ebp) ; comparamos el primer parametro de la pila (entero con la cantidad de parametros) | |
jle .L7 ; y si es menor o igual a 2, saltamos a .L7 | |
.L6: ; etiqueta del segmento de codigo | |
movl $.LC0, (%esp) ; movemos la cadena almacenada en .L0 al tope de la pila | |
call puts ; llamamos a puts | |
jmp .L8 ; saltamos a .L8 | |
.L7: ; etiqueta del segmento de codigo | |
movl 12(%ebp), %eax ; movemos el segundo parametro de la funcion al registro %eax | |
addl $4, %eax ; le agregamos 4bytes | |
movl (%eax), %eax ; mover el valor al cual apunta %eax a la direccion del registro %eax | |
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp (tope de pila) | |
call atoi ; call to atoi | |
movl %eax, 24(%esp) ; asignamos el valor de %eax a la variable %esp+24 ('n') | |
movl $0, 28(%esp) ; asignamos un 0 al registro %esp+28 (resultado = 0) | |
movl 24(%esp), %eax ; movemos el valor del registro esp+24 (n) al registro %eax | |
movl %eax, (%esp) ; movemos el registro %eax al tope de la pila (enviar un parametro) | |
call factorialRec ; llamamos a la funcion factorialRec | |
movl %eax, 28(%esp) ; asignamos lo que regrese la llamada a la funcion a la variable %esp+28 (resultado = factorialRec(n)) | |
movl $.LC1, %eax ; movemos la cadena almacenada en .LC1 al registro %eax | |
movl 28(%esp), %edx ; movemos el valor del registro %esp+28 a %edx que es un registro de entrada y salida | |
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4 | |
movl %eax, (%esp) ; movemos la direccion de %eax al valor que apunta %esp (tope de la pila) | |
call printf ; call to printf | |
.L8: ; etiqueta del segmento de codigo | |
movl $0, %eax ; asignamos el valor de 0 a %eax (para retornar) | |
leave ; epilogo de la funcion | |
ret ; regresamos el control a la funcion padre | |
.LC0: ; etiqueta del segmento de codigo | |
.string "BAD ARGUMENTS, expected 1\n" ; cadena almacenada en ese segmento | |
.LC1: ; etiqueta del segmento de codigo | |
.string "%d\n" ; cadena almacenada en ese segmento |
Lo siguiente son las diapositivas que explique en clase, lo dejo como evidencia y por si quieren visualizar algo, la mayoria de la informacion que les comparto se encuentra en la siguiente liga: Lenguaje Ensamblador Teoría
Cualquier comentario, sugerencia o aclaracion, dejen un comentario.
Referencias: AQUI
No hay comentarios:
Publicar un comentario