son - Implementación de funciones anidadas
funcion si excel varias condiciones (1)
GCC usa algo llamado trampolín.
Información: http://gcc.gnu.org/onlinedocs/gccint/Trampolines.html
Un trampolín es una pieza de código que GCC crea en la pila para usar cuando necesita un puntero a una función anidada. En su código, el trampolín es necesario porque pasa g
como parámetro a una llamada de función. Un trampolín inicializa algunos registros para que la función anidada pueda referirse a variables en la función externa, luego salta a la función anidada. Los trampolines son muy pequeños: "rebotas" de un trampolín y en el cuerpo de la función anidada.
El uso de funciones anidadas de esta manera requiere una pila ejecutable, que se desaconseja en estos días. Realmente no hay forma de evitarlo.
Disección de un trampolín:
Aquí hay un ejemplo de una función anidada en C extendida de GCC:
void func(int (*param)(int));
void outer(int x)
{
int nested(int y)
{
// If x is not used somewhere in here,
// then the function will be "lifted" into
// a normal, non-nested function.
return x + y;
}
func(nested);
}
Es muy simple para que podamos ver cómo funciona. Aquí está el ensamblaje resultante del outer
, menos algunas cosas:
subq $40, %rsp
movl $nested.1594, %edx
movl %edi, (%rsp)
leaq 4(%rsp), %rdi
movw $-17599, 4(%rsp)
movq %rsp, 8(%rdi)
movl %edx, 2(%rdi)
movw $-17847, 6(%rdi)
movw $-183, 16(%rdi)
movb $-29, 18(%rdi)
call func
addq $40, %rsp
ret
Notarás que la mayoría de lo que hace es escribir registros y constantes en la pila. Podemos seguirnos y encontrar que en SP + 4 coloca un objeto de 19 bytes con los siguientes datos (en sintaxis de GAS):
.word -17599 .int $nested.1594 .word -17847 .quad %rsp .word -183 .byte -29
Esto es bastante fácil de ejecutar a través de un desensamblador. Supongamos que $nested.1594
es 0x01234567
y %rsp
0x0123456789abcdef
es 0x0123456789abcdef
. El desensamblaje resultante, provisto por objdump
, es:
0: 41 bb 67 45 23 01 mov $0x1234567,%r11d 6: 49 ba ef cd ab 89 67 mov $0x123456789abcdef,%r10 d: 45 23 01 10: 49 ff e3 rex.WB jmpq *%r11
Entonces, el trampolín carga el puntero de pila de la función externa en %r10
y salta al cuerpo de la función anidada. El cuerpo de la función anidada se ve así:
movl (%r10), %eax
addl %edi, %eax
ret
Como puede ver, la función anidada usa %r10
para acceder a las variables de la función externa.
Por supuesto, es bastante tonto que el trampolín sea más grande que la función anidada en sí misma. Usted podría hacerlo mejor. Pero no mucha gente usa esta característica, y de esta manera, el trampolín puede permanecer del mismo tamaño (19 bytes) sin importar qué tan grande sea la función anidada.
Nota final: en la parte inferior de la asamblea, hay una directiva final:
.section .note.GNU-stack,"x",@progbits
Esto indica al vinculador que marque la pila como ejecutable.
Recientemente descubrí que gcc permite la definición de función anidada. En mi opinión, esta es una característica interesante, pero me pregunto cómo implementarla.
Si bien no es difícil implementar llamadas directas de funciones anidadas pasando un puntero de contexto como un argumento oculto, gcc también permite tomar un puntero a una función anidada y pasar este puntero a una función arbitraria que a su vez puede llamar al anidado función del contexto. Como la función que llama a la función anidada solo tiene el tipo de función anidada para llamar, obviamente no puede pasar un puntero de contexto.
Sé que otros lenguajes como Haskell que tienen una convención de llamadas más intrincada permiten la aplicación parcial para respaldar tales cosas, pero no veo forma de hacerlo en C. ¿Cómo es posible implementar esto?
Aquí hay un pequeño ejemplo de un caso que ilustra el problema:
int foo(int x,int(*f)(int,int(*)(void))) {
int counter = 0;
int g(void) { return counter++; }
return f(x,g);
}
Esta función llama a una función que llama a una función que devuelve un contador del contexto y lo incrementa al mismo tiempo.