Computadores de Programação (MAB353) Aula 7: 29 de abril de 2010
1 2
Subrotinas Um procedimento ou função é uma subrotina armazenada que executa uma tarefa específica baseada nos parâmetros de entrada Ferramenta de programação que facilita o reuso de código e torna os programas mais fáceis de entender e manter
Passos para execução de subrotinas 1 Colocar os parâmetros em um local onde a subrotina poderá acessá-los 2 Transferir o controle para a subrotina 3 Adquirir recursos de armazenamento necessários para a subrotina 4 Executar a tarefa desejada 5 Colocar os resultados em um local onde o programa pode acessá-los 6 Devolver o controle para o ponto de origem (onde a subrotina foi chamada)
Alocação de registradores O MIPS segue as seguintes convenções para alocação/uso de registradores nas chamadas de subrotinas: $a0 $a3 : 4 registradores para passagem de parâmetros $v0 $v1 : 2 registradores para os valores de retorno $ra: 1 registrador de endereço de retorno
Instrução de desvio para subrotinas Instrução jal O MIPS define uma instrução especial para execução de subrotinas: jal (jump-and-link)...desvia para um endereço e simultaneamente salva o endereço da instrução seguinte (endereço de retorno) em $ra ex., jal Soma Instrução jr Para implementar o retorno ao ponto onde a subrotina foi chamada, a instrução jr é usada ex., jr $ra
Resumo da execução de subrotinas no hardware O programa coloca os parâmetros da subrotina em $a0, $a1, $a2, $a3 e e chama jal X para desviar para a subrotina X A subrotina executa, coloca os resultados em $v0, $v1 e devolve o controle para o programa fazendo jr $ra
Ponteiro de programa Associado a idéia de programa armazenado na memória, está a necessidade de ter um registrador que armazene sempre o endereço da instrução atual Esse registrador é o ponteiro de programa (abreviado PC no MIPS) A instrução jal salva o valor de PC + 4 no registrador $ra
O que fazer quando são necessários mais registradores além dos 4 para passagem de parâmetro e os 2 de retorno?...como a subrotina não pode deixar marcas após a sua execução, qualquer registrador excedente deve ser restaurado ao término de execução da subrotina A estrutura ideal para manter cópias temporárias dos valores dos registradores é a pilha (último a entrar, primeiro a sair) A pilha precisa de um ponteiro para o endereço no topo (mais recentemente usado)...no topo da pilha é onde devem ser guardados os valores atuais dos registradores, e onde os valores antigos dos registradores são encontrados para serem restaurados
Ponteiro da pilha O ponteiro da pilha (stack pointer) é decrementado de 4 bytes a cada registrador salvo e incrementado de 4 bytes a cada registrador retirado da pilha...por precedentes históricos, as pilhas crescem de endereços mais altos para endereços mais baixos essa convenção significa que acrescentamos valores na pilha subtraindo seu ponteiro de pilha e retiramos valores da pilha somando seu ponteiro de pilha O registrador $sp é reservado pelo MIPS para ponteiro de pilha
Exemplo de subrotina C
Exemplo de implementação da subrotina C em MIPS
Atualização do ponteiro da pilha High address $sp Contents of register $t1 Contents of register $t0 $sp $sp Contents of register $s0 Low address a. b. c. FIGURE2.10 The values of the stack pointer and the stack (a) before, (b) during, and (c) aft er the procedure call. The stack pointer always points to the top of the stack, or the last word in the stack in this drawing. Copyright 2009 Elsevier, Inc. All rights reserved.
Registradores temporários Para evitar a necessidade de sempre salvar e restaurar registradores auxiliares, o MIPS separa (convenciona) 18 registradores em 2 grupos: 1 $t0 $t9: 10 registradores temporários cujos valores não são preservados após as chamadas de subrotinas 2 $s0 $s7: 8 registradores que devem ser preservados nas chamadas de subrotinas (i.e., se usados devem ser armazenados e restaurados no final)
Uma solução é salvar Prof. Silvana todos Rossettos registradores Implementação de sub-rotinas que precisam no MIPS ser Sumário Exemplo Subrotinas podem invocar outras subrotinas, inclundo elas próprias (recursão) O tratamentode chamadas aninhadas requer atenção especial Suponha que o programa chame a subrotina A com o argumento de valor 3, colocado em $a0 e A chame B com argumento de valor 7, também colocado em $a0 Como A ainda não terminou sua tarefa, há um conflito sobre o uso do registrador $a0 Além disso, a subrotina A não é mais capaz de retornar ao ponto onde foi chamada porque o registrador $ra foi sobreposto com o endereço de retorno de B
Exemplo de recursão em C
Exemplo de implementação da recursão C em MIPS
Pilha e as chamadas recursivas Stack Old $ra Old $fp main Old $a0 Old $ra Old $fp Old $a0 Old $ra Old $fp Old $a0 Old $ra Old $fp Old $a0 Old $ra Old $fp fact (10) fact (9) fact (8) fact (7) Stack grows FIGURE B.6.3 Stack frames during the call of fact(7). Copyright 2009 Elsevier, Inc. All rights reserved.
A linguagem C tem duas classess de armazenamento de variáveis: automático e estático Variáveis automáticas são internas de uma subrotina, e são descartadas quando a subrotina termina Variáveis estáticas são externas de uma subrotina, e permanecem entre chamadas de subrotinas Variáveis C declaradas com a palavra chave static ou declaradas fora de uma subrotina são consideradas estáticas (e são armazenadas fora da pilha)
Acesso a dados estáticos Para facilitar o acesso a dados estáticos, o MIPS reserva outro registrador: ponteiro global (global pointer) ($gp) O $gp aponta para a área de memória reservada para variáveis estáticas
Alocando espaço para novos dados na pilha A pilha também é usada para armazenar variáveis que são locais (internas) a subrotina mas não cabem nos registradores, como vetores ou structs O segmento da pilha que contém valores de registradores para restauração e variáveis locais é chamado procedure frame ou registro de ativação Programas MIPS podem usar um ponteiro de frame (frame pointer) ($fp) para apontar para a primeira palavra da frame de subrotina A idéia é oferecer um registrador de base estável dentro de uma subrotina para referenciar a memória local, para os casos em que o ponteiro de pilha pode variar durante a execução da subrotina (em que caso isso pode acontecer?)
Ocupação da pilha High address $fp $fp $sp $fp Saved argument registers (if any) $sp Saved return address Saved saved registers (if any) $sp Local arrays and structures (if any) Low address a. b. c. FIGURE 2.12 Illustra tion of the stack allocation (a) before, (b) during, and (c) aft er the procedure call. The frame pointer ($fp) points to the first word of the frame, often a saved argument register, and the stack pointer Prof. ($sp) Silvana points Rossetto to the top Implementação of the stack. de The sub-rotinas stack is adjusted no MIPSto make room for
Alocando espaço para novos dados na heap Além das variáveis locais, programadores C precisam espaço em memória para variáveis estáticas e estruturas de dados dinâmicas
Organização da memória 7fff fffc hex Stack segment 10000000 hex 400000 hex Dynamic data Static data Reserved Data segment Text segment FIGURE B.5.1 Layout of memory. Copyright 2009 Elsevier, Inc. All rights reserved.
Organização da memória A parte mais baixa da memória é o segmento de texto Depois o segmento de dados estáticos (constantes, globais, estáticas) Depois a heap (segmento para estruturas de dados que mudam ao longo da execução do programa, ex., com uso de malloc em C e new em Java) O ponteiro global $gp é alocado no meio do segmento de dados
1 Organização e projeto de computadores: a interface hardware/software, D. A. Patterson e J. L. Hennessy, Elsevier, 2005.