Digamos que tengo tres funciones, f1(), f2() y f3(). Cuando se llama a f1, almacena información en los registros de la CPU (e imagino que también hay otra información importante). Ahora, dependiendo de una condición desconocida en tiempo de compilación, f1 llamará a f2 o f3. f2 y f3 usan registros muy diferentes, algunos de los cuales pueden superponerse con los utilizados por f1. ¿Es correcto el siguiente razonamiento?

El compilador sabe qué registros necesita una función particular durante su ejecución. Por lo tanto, cuando f1 llama a f2 o f3, el código de llamada a la función conserva los registros que f2 o f3 usan en la pila, independientemente de si no están siendo utilizados por f1.

¿O hay algún otro mecanismo por el cual el compilador conserva los registros para que la función a la que se devuelve no pierda sus datos?

4
Michael Stachowsky 24 dic. 2016 a las 16:26

3 respuestas

La mejor respuesta

Creo que, como otros han dicho, los argumentos para una función generalmente se envían a través de varios registros (a partir de entonces en la pila). Los registros que se utilizan dependen del compilador: para gcc ver GNU C / ensamblador: http://cs.lmu.edu/~ray/notes/gasexamples/

Una serie de principios dignos de mención:

  1. marco de pila

  2. llamante (la función que llama f1) y funciones de llamada (sus funciones f1, f2 ...)

  3. Registros volátiles y no volátiles. Para su pregunta, no necesita preocuparse por los registros no volátiles.

Cada función tiene un marco de pila, este es un bloque expandible de la pila que almacena temporalmente datos que deben cargarse dentro y fuera de los registros.

Antes de cada llamada a la función (a la persona que llama desde la persona que llama), los valores que desea transmitir, es decir, sus argumentos, se colocarán en una serie de registros predeterminados (generalmente 4-6 dependiendo del compilador; vea el enlace); Si hay más argumentos que el número de registros predeterminados, estos valores adicionales se almacenan en la pila (normalmente, el marco de la pila de llamantes).

Si la persona que llama utiliza estos registros predeterminados, el compilador introducirá estos valores en el marco de la pila de la persona que llama antes de asignar los argumentos a los registros antes de hacer la llamada a la persona que llama (por ejemplo, su función f1). Una vez que la función llamada (llamada) regresa, estos valores se restauran a sus respectivos registros desde la pila.

No importa cómo o en qué orden se siga una serie de funciones al mismo sistema cuando el compilador convierte su código C en ensamblado / código operativo.

4
cdcdcd 29 dic. 2016 a las 21:23

"El compilador sabe qué registros necesita una función particular durante su ejecución".

No, generalmente no sabrá esto.

Por una razón, una función puede ser de una biblioteca (de terceros) sobre la cual el compilador no sabe nada. Por otra razón, ¿qué pasa si esa función llama a otra función y a otro etetera?

El compilador simplemente empujará todos los registros "sospechosos" a la pila y los desplegará antes de regresar.

5
Paul Ogilvie 24 dic. 2016 a las 13:31

Recuerde que un lenguaje de programación es una especificación en un documento. Para C11, lea n1570.

Los registros no existen en C (en otras palabras, la palabra clave register casi obsoleta ya no está relacionado con los registros del procesador). Solo importan en el código de máquina (a menudo generado por un compilador de C).

Sin embargo, el código generado por un compilador determinado (para un conjunto de instrucciones y un sistema de destino determinados) obedece a algunas convenciones, en particular convenciones de llamada y el ABI (lea el sistema V x86-64 ABI que rige Linux para un ejemplo). Estas convenciones definen cómo se deben usar los registros y qué registros se guardan o se guardan. La asignación de registros es una parte difícil de un optimización del trabajo del compilador.

A menudo, el compilador emitirá código para derramar parte del contenido de los registros en la pila de llamadas. Y un registro dado puede usarse para varias cosas (por ejemplo, podría mantener dos variables diferentes, si ocurren en diferentes lugares en la misma función).

En general, la convención de llamada no depende de la función llamada (recuerde que puede hacer llamadas indirectas a través de punteros de función), sino principalmente de su firma.

6
Basile Starynkevitch 25 dic. 2016 a las 09:08