Subrutinas Parámetros Lenguajes de Programación I Subrutinas - Pasaje de Parámetros Ernesto Hernández-Novich <[email protected]> c 2006-2010 Copyright Subrutinas Parámetros Subrutinas Mecanismo principal para abstracción de control. Asocian un nombre a una secuencia de instrucciones. Comportamiento varía según parámetros. Funciones – cuando retornan valores. Puras – No causan efectos de borde. Impuras – Causan efectos de borde. Procedimientos – cuando no retornan valores. En la mayoría de los lenguajes, deben ser declaradas antes de ser utilizadas. Subrutinas Parámetros Registro de Activación Profundizando lo que ya estudiamos Cada rutina invocada tiene un Registro de Activación en la Pila de Ejecución. Se arma en parte por el llamador y en parte por el llamado. Se desarma cuando la rutina retorna. Construcción basada en aprovechar características del hardware subyacente. Stack Pointer – registro que apunta al último elemento utilizado de la Pila de Ejecución. Frame Pointer – registro que apunta a una ubicación fija dentro del Registro de Activación. CALL y RETURN – auxilian al código generado por el compilador. En lenguajes dinámicos parte o todo este comportamiento debe ser simulado en el heap. Subrutinas Parámetros Registro de Activación La importancia de ser Frame Pointer Posiciones de objetos expresada como desplazamientos a partir del frame pointer. Objetos con Tamaño Fijo Desplazamientos son conocidos a tiempo de compilación. Ubicados cerca del frame pointer – ambos “lados”. Acceso directo a tiempo de ejecución. Objetos con Tamaño Variables Desplazamientos desconocido a tiempo de compilación. Ubicados lejos del frame pointer – ambos “lados”. Acceso indirecto a tiempo de ejecución (dope vector). Aplica para argumentos y variables – orientación diferente Locales – después de la parte estática en el llamado. Argumentos – antes de la parte estática en el llamador. Subrutinas Parámetros Cadenas Dinámica y Estática Criterios de inclusión y dependencia de ejecución Cadena Dinámica – representa el orden de llamadas Se mantiene conservando en cada Registro de Activación, el apuntador al Registro de Activación anterior. Responsabilidad del llamado a tiempo de ejecución. Indispensable para alcance dinámico y excepciones. La Cadena Estática – representa el anidamiento de rutinas (en lenguajes donde tenga sentido). Responsabilidad del llamador a tiempo de compilación. Indispensable para alcance estático a objetos no locales. Aprovecha acceso indirecto del procesador de ser posible. Subrutinas Parámetros Mantenimiento del Registro de Activación Responsabilidad compartida entre llamador y llamado Usualmente tres fragmentos de código generados a tiempo de compilación: Secuencia de Llamada – código ejecutado por el llamador inmediatamente antes de transferir el control a la subrutina. Prólogo – código ejecutado por el llamado justo al entrar en la subrutina. Epílogo – código ejecutado por el llamado justo antes de retornar de la subrutina. Subrutinas Parámetros ¿Qué se hace en la Secuencia de Llamada? Salvar registros cuyo valor sea necesario al retornar. Evaluar y colocar parámetros en la pila – tamaño variable van antes que tamaño fijo. Reserva espacio para valores de retorno en caso que usen la pila (si aplica). Calcular Cadena Estática y colocarla en la pila (si aplica). Saltar a la Subrutina (instrucción especial si es posible). La subrutina ejecuta y eventualmente retorna. Tomar los valores de retorno (si aplica). Restaurar registros salvados. Subrutinas Parámetros ¿Qué se hace en el Prólogo? Salvar valor actual del frame pointer – esto extiende la cadena dinámica. Apuntar el frame pointer a la ubicación apropiada – usualmente el tope actual de la pila. Reservar espacio en la pila para las variables locales – modificación directa del stack pointer. Salvar registros que sean utilizados en la subrutina. Subrutinas Parámetros ¿Qué se hace en el Epílogo? Colocar el valor de retorno en la posición adecuada del Registro de Activación del llamador (si aplica). Restaurar registros que fueron utilizados en la subrutina. Restaurar los valores del stack pointer y frame pointer. Asignar frame pointer al stack pointer. Desempilar frame pointer. Retornar el control al llamador – instrucción especial si es posible. Subrutinas Parámetros ¿Cómo se mantiene la Cadena Estática? A tiempo de compilación se sabe El anidamiento de rutinas. Quién llama a quién. Cada vez que el compilador va a generar código para una llamada, debe hacer una observación simple: Si el llamado está anidado dentro del llamador: La Cadena Estática del llamado debe apuntar al llamador. Trivial – basta copiar el frame pointer propio como cadena estática del llamado. Si el llamado está k ≥ 0 alcances hacia afuera: k alcances envuelven al llamador. El llamador debe seguir su cadena estática k veces y copiar ese valor como cadena estática del llamado. Subrutinas Parámetros Pasaje de Parámetros Permiten influir sobre el comportamiento de la subrutina. Formales – indicados simbólicamente en la definición proc foo(int bar, bool baz, float qux) Reales – pasados efectivamente a tiempo de ejecución foo(42,true,3.1415) También llamados argumentos o actuales. Invocación Notación prefija en la mayoría de los lenguajes. LISP asume que primer símbolo de expresión es la función. Notación infija opcional (ML, Haskell). Notación mezclada como en Smalltalk. Subrutinas Parámetros Modalidades para el Pasaje de Parámetros Pasaje por Valor (Call by Value) Pasaje por Valor Implantación – simple Calcular el valor del parámetro actual. Espacio para el formal en la pila recibe una copia del valor calculado. Puede optimizarse usando un registro en lugar de espacio en la pila. Ventaja – imposible que la rutina modifique los actuales. Desventaja – costoso pasar objetos grandes. Subrutinas Parámetros Modalidades para el Pasaje de Parámetros Pasaje por Referencia Pasaje por Referencia Implantación – simple Calcular la dirección del parámetro actual. Espacio para el formal en la pila recibe una copia del valor calculado – es una referencia. Se usa en la subrutina como un l-value. Ventaja – eficiente, pues no hay copia de valores ni requiere espacio adicional. Desventajas Acceso más lento – al menos una indirección. Los actuales deben ser l-values. Crean aliases. Subrutinas Parámetros La diferencia... x : integer procedure foo( y : integer ) y := 3; print x; end x := 2 foo(x) print x Si y es pasada por valor, imprime 2 2. Si y es pasada por referencia, imprime 3 3. Subrutinas Parámetros Variaciones en las Modalidades ¿Por valor o por referencia? Lenguajes que sólo pasan por valor – la mayoría. Simular pasaje por referencia pasando un apuntador. Casos especiales (arreglos en C). Lenguajes que sólo pasan por referencia – Fortran. Lenguajes que ofrecen ambos mecanismos a través de palabras reservadas – (var en Pascal). Subrutinas Parámetros Variaciones en las Modalidades Parámetros Sólo-Lectura (Modula-3, C) Eficiencia de pasaje por referencia. Seguridad de pasaje por valor. Se indica en declaración del formal que no pueda ser modificado dentro de la subrutina. Dirección del Pasaje (Ada) in, out y inout. Los parámetros deben ser copiados al entrar. in es equivalente a pasaje por valor. out es denominado llamada por resultado. inout es denominado llamada por valor/resultado. El efecto de los cambios para un inout puede o no ser inmediato. Subrutinas Parámetros Clausuras En lenguajes que permiten pasar subrutinas como parámetros actuales Debe pasarse la referencia al cuerpo de la subrutina. Debe pasarse el ambiente de referencia solamente si el lenguaje soporta anidamiento de subrutinas. Rutinario y simple en lenguajes funcionales porque los ambientes de referencia sólo interesan por sus valores. En lenguajes imperativos el ambiente de referencia incluye referencias a los objetos alcanzables al momento de construir la clausura. Subrutinas Parámetros Parámetros de Propósito Especial Arreglos con Forma Dinámica (Conformant Arrays). Cuando la forma del arreglo sólo se conoce a tiempo de ejecución. Pasar una referencia a la base y las dimensiones. Valores por defecto para parámetros omitidos. Parámetros nombrados (keyword parameters) vs. parámetros posicionales. Número variable de argumentos. Recibir todo como una lista y manipularla (LISP, Perl). Utilizar macros/librerías para acceder a ellos en la pila de ejecución (C). Obligar a que todos sean del mismo tipo (C#, Java). Subrutinas Parámetros Valores de Retorno La mayoría de los lenguajes son restrictivos al respecto: Un valor escalar. ...que podría ser un apuntador. Lenguajes más flexibles permiten retornar elementos de tipos compuestos (registros y listas) o funciones. ¿Cómo retornarlo? Instrucción explícita de retorno (return). Valor de la última expresión evaluada.