Conjunto de Instruções. Conjunto de Instruções



Documentos relacionados
Conjunto de Instruções

MIPS. Prof. Carlos Bazilio

Arquitetura de Computadores. Linguagem de Máquina

Computadores de Programação (MAB353)

Organização e Arquitetura de Computadores I

Aula 14: Instruções e Seus Tipos

NOTAS DE AULA Prof. Antonio Carlos Schneider Beck Filho (UFSM) Prof. Júlio Carlos Balzano de Mattos (UFPel) Arquitetura de Von Neumann

Computadores de Programação (MAB353)

Geração de código. Ivan Ricarte INTRODUÇÃO À COMPILAÇÃO

MODOS DE ENDEREÇAMENTO

Arquitetura de Computadores

Computadores de Programação (MAB353)

Organização e Arquitetura de Computadores I

CAPÍTULO 7 NÍVEL DE LINGUAGEM DE MONTAGEM

Componentes do Computador e. aula 3. Profa. Débora Matos

PARTE II - CONJUNTO DE INSTRUÇÕES ARQUITETURA DE COMPUTADORES ANTONIO RAMOS DE CARVALHO JÚNIOR

Geração de código intermediário. Novembro 2006

Conjunto de instruções do CPU. Arquitectura de um computador. Definição das instruções (1) Definição das instruções (2)

ARQUITETURA DE COMPUTADORES

Organização de Computadores 1

Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores.

Arquitetura de Sistemas Digitais (FTL066) Instruções: Linguagem do Computador Segunda Lista de Exercícios

INTRODUÇÃO ÀS LINGUAGENS DE PROGRAMAÇÃO

Conjunto de Instruções e Arquitectura p.1

Introdução à Arquitetura de Computadores

Arquitetura de Computadores. Tipos de Instruções

Arquitetura de Computadores - Arquitetura RISC. por Helcio Wagner da Silva

Implementação de um soft-core em VHDL baseado no conjunto de instruções MIPS-I

Organização e Arquitetura de Computadores I

Programação de Computadores III

PROGRAMAÇÃO ESTRUTURADA. CC 2º Período

Análises Geração RI (representação intermediária) Código Intermediário

Arquiteturas RISC. (Reduced Instructions Set Computers)

BARRAMENTO DO SISTEMA

ULA Sinais de Controle enviados pela UC

3. O NIVEL DA LINGUAGEM DE MONTAGEM

Anotações da 2a Edição

3/9/2010. Ligação da UCP com o barramento do. sistema. As funções básicas dos registradores nos permitem classificá-los em duas categorias:

Arquitetura de Computadores I

Caminho dos Dados e Atrasos

Conjunto de. Instrução MIPS. Parte II. Instruções MIPS. Instruções MIPS. Instruções MIPS :: Instruções lógicas. :: Instruções lógicas

Conjunto de instruções. O Conjunto de Instruções. Conjunto de instruções. Instruções típicas. Instruções típicas. Instruções típicas

ARQUITETURA DE COMPUTADORES

PROJETO LÓGICO DE COMPUTADORES Prof. Ricardo Rodrigues Barcelar

Arquitetura de Rede de Computadores

2 Formalidades referentes ao trabalho

Aula 26: Arquiteturas RISC vs. CISC

Introdução. Introdução. Introdução. Organização Estruturada de Computadores. Introdução. Máquinas Multiníveis

Introdução às Linguagens de Programação

Unidade 10: A Unidade Lógica Aritmética e as Instruções em Linguagem de Máquina Prof. Daniel Caetano

O Processador: Caminho de Dados e Controle

2. OPERADORES ALGORITMOS, FLUXOGRAMAS E PROGRAMAS FUNÇÕES... 10

Linguagem de Montagem Funcionamento de CPU e Assembly Rudimentar

Introdução à Lógica de Programação

Unidade Central de Processamento (CPU) Processador. Renan Manola Introdução ao Computador 2010/01

O que é um programa? Programa é uma lista de instruções que descrevem uma tarefa a ser realizada pelo computador.

Algoritmos e Programação Estruturada

Programando o computador IAS

Curso: Ciência da Computação Disciplina: Construção de Compiladores Período: Prof. Dr. Raimundo Moura

Linguagem de Montagem

Java. Marcio de Carvalho Victorino

Capítulo 4. MARIE (Machine Architecture Really Intuitive and Easy)

Algoritmos e Programação (Prática) Profa. Andreza Leite andreza.leite@univasf.edu.br

Orientação a Objetos

Tabela de Símbolos. Análise Semântica A Tabela de Símbolos. Principais Operações. Estrutura da Tabela de Símbolos. Declarações 11/6/2008

Prof. Marcos Quinet Universidade Federal Fluminense UFF Pólo Universitário de Rio das Ostras - PURO

1.1. Organização de um Sistema Computacional

SSC0114 Arquitetura de Computadores

Computadores XXI: Busca e execução Final

ORGANIZAÇÃO E ARQUITETURA DE COMPUTADORES I

AMBIENTE PARA AUXILIAR O DESENVOLVIMENTO DE PROGRAMAS MONOLÍTICOS

Dadas a base e a altura de um triangulo, determinar sua área.

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP)

O processador é composto por: Unidade de controlo - Interpreta as instruções armazenadas; - Dá comandos a todos os elementos do sistema.

AULA 05: LINGUAGEM DE MONTAGEM: SUPORTE A PROCEDIMENTOS

Parte II Introdução a Linguagens de Programação

Edeyson Andrade Gomes

e à Linguagem de Programação Python

Organização e Arquitetura de Computadores I. de Computadores

Organização e Arquitetura de computadores


MIPS ISA (Instruction Set Architecture)

Processadores BIP. Conforme Morandi et al (2006), durante o desenvolvimento do BIP, foram definidas três diretrizes de projeto:

Conceitos de Linguagens de Programação

3. Arquitetura Básica do Computador

ArchC. Wesley Nunes Gonçalves

5 - Vetores e Matrizes Linguagem C CAPÍTULO 5 VETORES E MATRIZES

Arquitetura de Computadores. Assembly Miscelâneas. Mário O. de Menezes.

ARQUITECTURA DE COMPUTADORES CAPÍTULO II AULA X

Organização e Projetos de Computadores. Capítulo 2. Organização e Projetos de Computadores. Instruções

Aula 4 Pseudocódigo Tipos de Dados, Expressões e Variáveis

Geração e Otimização de Código

OPERADORES E ESTRUTURAS DE CONTROLE

28/9/2010. Unidade de Controle Funcionamento e Implementação

Algoritmos e Estruturas de Dados I 01/2013. Estruturas Condicionais e de Repetição (parte 2) Pedro O.S. Vaz de Melo

Programação WEB I Estruturas de controle e repetição

Curso: Técnico de Informática Disciplina: Redes de Computadores. 1- Apresentação Binária

Nível da Arquitetura do Conjunto das Instruções

Transcrição:

Conjunto de Instruções It is easy to see by formal-logical methods that there exist certain [instruction sets] that are in abstract adequate to control and cause the execution of any sequence of operations... The really decisive considerations from the present point of view, in selection an [instruction set], are more of a practical nature: simplicity of the equipment demanded by the [instruction set], and the clarity of its application to the actually important problems together with the speed of its handling of those problems. Burks, Goldstine and von Neumann, 1947 Conjunto de Instruções As linguagens de máquina são bastante parecidas entre si. Aprendendo bem uma é fácil aprender outra. Isto ocorre porque: Todas são baseadas nos mesmos princípios (arquitetura de von Neumann); Existe um conjunto de operações básicas que todas as máquinas devem fornecer; Projetistas têm um mesmo objetivo: encontrar uma linguagem que torne fácil a construção do hardware e de compiladores, maximizando a performance e minimizando os custos SIMPLICIDADE

Desta maneira é necessário um processo de Tradução Tradução As linguagens LBNs são definidas por uma série de Mnemônicos, que são, basicamente, símbolos que representam código binários Por exemplo, no caso do MIPS a instrução de adição é representada por add a, b, c esta instrução determina que o processador some o conteúdo de dois registradores (b, c) e coloque o resultado em outro registrador (a) A instrução ADD segue um formato bem definido: código correspondente ao mnemônico da instrução add identificação do registrador destino identificação do primeiro operando identificação do segundo operando Assembler x Linguagem de Máquina Entretanto, para que um programa seja entendido pela máquina, é necessário que suas instruções estejam codificadas na forma binária, isto é, na forma de 0s e 1s. No MIPS, a instrução add corresponde ao código binário 000000100000 O conjunto de instruções de uma arquitetura (na forma de Mnemônicos) corresponde à Linguagem de Montagem da arquitetura (Linguagem ASSEMBLY) O conjunto de instruções de uma arquitetura (na forma binária) corresponde à Linguagem de Máquina

Tradutores Tanto os programas implementados em LANs como em LBN precisam ser traduzidos para a linguagem de máquina do processador O processo de tradução de uma linguagem de alto nível (LAN) para linguagem de máquina é feito compiladores ou interpretadores O processo de tradução de uma linguagem de Montagem para linguagem de máquina é feito por tradutores denominados de Montadores (ou Assemblers). Compiladores e Interpretadores Compiladores são tradutores que após várias fases (análise léxica, análise sintática, análise semântica, geração de código intermediário, otimização de código e geração de código de montagem) geram um programa executável. Na verdade, este programa executável deverá ser carregado em memória para ser executado. Quem faz esta tarefa é um programa do sistema operacional (programa carregador ou loader) Interpretadores não geram código executável Os interpretadores traduzem cada instrução do programa (em LAN ou Assembly) e a executam

fonte (LAN ou Assembly) Interpretador execução Conjunto de Instruções O conjunto de instruções que veremos vem do MIPS, utilizado por diversas empresas (NEC, Nintendo, Silicon Graphics, Sony, ) Utilizaremos um simulador de MIPS chamado SPIM, que tem versões para Unix, Windows e DOS. O SPIM pode ser baixado a partir da URL: http://www.mkp.com/cod2e.htm ISA do MIPS (simplificada) Categorias de Instruções: Load/Store Computação Jump e Desvio Ponto Flutuante Gerenciamento de Memória Especial 3 Formatos de Instrução: 32 bits Registradores R0 - R31 PC HI LO OP OP OP rs rt rd sa funct rs rt imediato Destino do jump

instrução por linha add a, a, d add a, a, e # a = b + c + d # a = b + c + d + e Operações do Hardware Exigir que toda instrução tenha exatamente três operandos condiz com a filosofia de manter o hardware simples: hardware para número va riável de parâmetros é mais complexo que para número fixo. Princípio #1 para projetos: Simplicidade favorece a regularidade Exemplo Qual o código gerado por um compilador C para o seguinte trecho? a = b + c; d = a e; add a, b, c sub d, a, e #a = b + c #d = a - e

sub f, t0, t1 #f (g h) (I j) Operandos e Registradores Ao contrário das linguagens de alto nível, operandos de instruções aritméticas não podem ser quaisquer va riáveis são escolhidos dentre um conjunto de registradores: Número limitado de endereços especiais construídos diretamente no hardware; Blocos básicos para construção de computadores, pois são primitivas usadas em projeto de hardware que também são vistas pelo programador; Registradores : benefícios Registradores no hardware, dentro do processador mais rápidos que memória Registradores são de mais fácil utilização por compiladores: como um local para armazenamento temporário podem armazenar variáveis para reduzir o tráfego de memória e melhorar a densidade de código (uma vez que os registradores podem ser especificados com menos bits que um endereço de memória)

Operandos e Registradores Apesar de podermos nos referir aos registradores através de números, em MIPS existe uma convenção de se utilizar nomes na forma $xy Usaremos: $s0, $s1, $s2, para registradores que correspondam a variáveis em C $t0, $t1, $t2, para registradores temporários necessários para compilar o programa em instruções MIPS Exemplo Qual o código gerado por um compilador C para o seguinte trecho? f = (g + h) (i + j) As variáveis f, g, h, i, j podem ser mapeadas nos registradores $s0, $s1, $s2, $s3 e $s4, respectivamente. add $t0, $s1, $s2 # temporário t0 = g + h add $t1, $s3, $s4 # temporário t1 = i + j sub $s0, $t0, $t1 # f = (g + h) (I + j)

fornecer um endereço de memória Memória Memória é somente um grande vetor unidimensional, com o endereço atuando como índice no vetor, começando em 0. 3 2 1 0 100 10 101 1 Processador Endereços Memória Dados Transferindo dados da memória A instrução de transferência de dados da memória para o registrador é chamada de load. Formato: Em MIPS, o nome da instrução é: lw (load word) lw registrador destino, constante (registrador base) Endereço de memória acessado é dado pela soma da constante (chamada de offset) com o conteúdo do registrador base

Vetor na memória Dados... 5 10 0 0 0 0 15 42...... 100 101 102 103 104 105 106 107... Endereço base de A Endereços Vetor A = [0,0,0,0,15], com 5 posições, começando no endereço de memória 102. Este endereço é chamado de endereço base do vetor. Assim, 102 é o endereço de A[0],103 o de A[1],...,106 o de A[4]. Exemplo Suponha que o vetor A tenha 100 posições, e que o compilador associou as variáveis g e h aos registradores $s1 e $s2. Temos ainda que o endereço base do vetor A é dado em $s3. Qual o código para g = h + A[8]? Primeiro temos que pegar o operando que está na memória e transferi-lo para um registrador: lw $t0, 8($s3) add $s1, $s2, $t0 # temporário t0 = A[8] # g = h + A[8]

Processador Endereços Dados Memória Vetor na memória (2) Cada posição do vetor (de inteiros) é uma palavra, e portanto ocupa 4 bytes Dados... 5 10 0 0 0 0 15 42...... 400 404 408 412 416 420 424 428... Endereço base de A Endereços Vetor A = [0,0,0,0,15], com 5 posições, começando no endereço de memória 408. Assim, 408 é o endereço de A[0],412 o de A[1], 416 o de A[2], 420 o de A[3] e 424 o de A[4]. Exemplo Suponha que o vetor A tenha 100 posições, e que o compilador associou a variável h ao registrador $s2. Temos ainda que o endereço base do vetor A é dado em $s3. Qual o código para: A[12] = h + A[8]? A nona posição do vetor A, A[8], está no offset 8 x 4 = 32 lw $t0, 32($s3) # temporário t0 = A[8] add $t0, $s2, $t0 # temporário t0 = h + A[8] A décima-terceira posição do vetor A, A[12], está no offset 12 x 4 = 48 lw $t0, 48($s3) # carrega A[12] em $t0!!!!

(chamada de offset) com o conteúdo do registrador base Endereço absoluto de A[3] Para obter o endereço absoluto precisamos: Variável i ( i = 3) ($s4) 0 1 2 3 4 5 6 7 8 9... Registrador base exe. $s2 deslocamento(offset) offset = 4*i endereço = $s2 + 4*3 Exemplo: variável de índice Suponha que o vetor A tenha 100 posições, e que o compilador associou as variáveis g, h e i aos registradores $s1, $s2 e $s4. Temos ainda que o endereço base do vetor A é dado em $s3. Qual o código para: g = h + A[i]? Precisamos primeiro calcular o endereço de A[i]. Antes de somar i ao endereço base de A, devemos multiplicar i por 4. Vamos fazer isto por enquanto da seguinte forma: add $t1, $s4, $s4 # $t1 = 2 * i add $t1, $t1, $t1 # $t1 = 4 * i

Resumo da Tradução g = h + A[i] add $t1, $s4, $s4 add $t1, $t1, $t1 add $t1, $t1, $s3 lw $t0, 0($t1) add $s1, $s2, $t0 # $t1 = 2 * i # $t1 = 4 * i # $t1 = endereço de A[i] # temporário $t0 = A[i] # g = h + A[i] Exercício Temos ainda que o endereço base do vetor A é dado em $s2, e que as variáveis i e g são dadas em $s0 e $s1, respectivamente. Qual o código para A[i+g] = g + A[i] A[0]?

add $t0, $t0, $s2 # $t0 = endereço de A[i + g] sw $t1, 0($t0) # A[i + g] = g + A[i] A[0] Utilizando os registradores Muitos programas têm mais variáveis do que o número de registradores presente na máquina. Compilador tenta manter as variáveis mais usadas nos registradores e coloca o resto na memória, utilizando loads e stores para mover os dados entre a memória e os registradores: Compiladores têm que utilizar os registradores de forma eficiente. Alinhamento de Dados na Memória MIPS requer que todas as palavras comecem em endereços que são múltiplos de 4 bytes Alinhado Não alinhado 0 1 2 3 Chamamos isto de alinhamento: objetos têm que ter endereços que sejam múltiplos de seus tamanhos.

Representando Instruções no Computador Números são armazenados no hardware na base 2, ou binária. Instruções podem ser representadas como números; na verdade, cada pedaço da instrução é um número, e o posicionamento lado a lado destes números é que formam a instrução. Existe uma convenção em MIPS para associar nomes de registradores a seus números: $s0, $s1,, $s7 16, 17,, 23 $t0, $t1,, $t7 8, 9,, 15 Representando Instruções no Computador... Linguagem de máquina para a instrução add $t0, $s1, $s2 0 17 18 8 0 32 Cada um destes segmentos é chamado de campo. O primeiro e o último campos, juntos, dizem que a instrução é uma adição. O segundo diz qual é o primeiro registrador fonte (17 = $s1) e o terceiro o segundo registrador fonte (18 = $s2). O quarto campo é o registrador destino (8 = $t0). O quinto campo não é usado nesta instrução, e por isto tem valor zero.

Todas as instruções em MIPS possuem 32 bits ( simplicidade favorece regularidade ). Formato Instrução MIPS Daremos nomes aos campos para simplificar a discussão: op rs rt rd shamt funct 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits op: operação básica da instrução (opcode); rs: primeiro registrador fonte; rt: segundo registrador fonte; rd: registrador destino, recebe o resultado da operação shamt: quantidade de shift (veremos no capítulo 4) funct: função; seleciona a variante específica da operação dada no campo op, também chamada de código de função Formato Instrução MIPS... Um problema acontece quando uma instrução necessita de campos mais longos que os mostrados neste exemplo. Ex.: a instrução lw precisa especificar dois registradores e uma constante. Se a constante for ser representada no campo de um dos registradores, o valor dela ficará limitado a 32 (2^5). Obviamente, este valor é pequeno demais para ser útil. Assim, temos um conflito entre o desejo de que todas as instruções tenham o mesmo tamanho e o desejo de que tenhamos um único formato de instruções.

de dados: tipo-i ou formato-i Formato Instrução MIPS... Formato tipo-i: op rs rt endereço 6 bits 5 bits 5 bits 16 bits O endereço de 16 bits significa que uma instrução lw pode carregar qualquer palavra dentro de uma região de ±2^15 ou 32768 bytes (2^13 ou 8192 palavras) em relação ao endereço no registrador base rs. Formato Instrução MIPS... Ex: lw $t0, 32($s3) #temporário $t0 = A[8] op rs rt endereço Neste exemplo, rs recebe 19 ($s3), rt recebe 8 ($t0) e o campo endereço recebe o valor 32. Op neste caso é 35 (lw). Em uma instrução de load, o campo rt determina o registrador destino!! Apesar do hardware ficar mais complexo ao utilizarmos diferentes formatos de instrução, podemos reduzir este aumento de complexidade ao mantermos certa similaridade entre os formatos (ex.: 3 primeiros campos nos formatos tipo-r e tipo-i são os mesmos; comprimento do último campo tipo-i é igual à soma dos 3 últimos tipo-r).

Codificação das Instruções Vistas Instrução Formato op rs rt rd shamt funct endereço add R 0 reg reg reg 0 32 não sub R 0 reg reg reg 0 34 não lw I 35 reg reg não não não ender. sw I 43 reg reg não não não ender. $s0, $s1,, $s7 16, 17,, 23 $t0, $t1,, $t7 8, 9,, 15 Exemplo de compilação manual Suponha que $t1 tenha o endereço base de A e que $s2 corresponda a h, traduza a seguinte linha em C para código de máquina MIPS: A[300] = h + A[300]; Primeiro, temos que o código em assembly correspondente é: lw $t0,1200($t1) add $t0, $s2, $t0 sw$t0, 1200($t1) # $t0 = A[300] # $t0 = h + A[300] # A[300] = h + A[300] Qual o código de máquina destas 3 instruções?

000000 10010 01000 01000 00000 100000 101011 01001 01000 0000 0100 1011 0000 Idéia geral: conceito de programa armazenado Computadores de hoje são construídos baseados em dois princípios fundamentais: Instruções podem ser representadas como números Programas podem ser armazenados na memória para serem lidos ou escritos da mesma forma que números Conceito de programa armazenado, fundamental para a Computação!!! Instruções para tomada de decisões O que distingue um computador de uma calculadora simples é a habilidade de tomar decisões. Com base na entrada e nos resultados computados, diferentes instruções são executadas. Em linguagens de alto nível, uma das formas de se tomar decisões é através das instruções if e goto. Em MIPS, temos duas instruções que atuam de maneira similar a instruções que combinam if com goto: beq registr1, registr2, L1 # branch if equal bne registr1, registr2, L1 # branch if not equal rótulo (label)

goto L1 Instruções de decisão no MIPS... bne registr1, registr2, L1 Semântica: desvie se (valores nos registradores) não são iguais em C, isto seria equivalente a: if (registr1!=registr2) goto L1 Estas instruções são chamadas de desvios condicionais. Exemplo Se as variáveis f, g, h, i, j correspondem aos registradores $s0 a $s4, qual é o código compilado para o seguinte trecho em C? if (i == j) goto L1; f = g + h; L1: f = f i; beq $s3, $s4, L1 # vá para L1 se $s3 == $s4 add $s0, $s1, $s2 # f = g + h (se i!= j) L1: sub $s0, $s0, $s3 # f = f i (se i == j) Como instruções são armazenadas na memória, elas têm endereços também!!

Desvio incondicional em MIPS MIPS tem um desvio incondicional: Chamada de instrução de salto (jump): salte para o rótulo especificado, incondicionalmente Em C, isto seria equivalente a: goto label Podemos pensar que isto é equivalente a: j rotulo beq $0,$0,rotulo Uma vez que a condição sempre é satisfeita Existe um formato de instrução para desvio (tipo-j ou formato-j), como veremos mais à frente. Exemplo: código para if Se as variáveis f, g, h, i, j correspondem aos registradores $s0 a $s4, qual é o código compilado para o seguinte trecho em C? if (i == j) f = g + h; else f = g h; Queremos implementar o seguinte fluxo: (verdadeiro) i == j i == j? (falso) i!= j f=g+h f=g-h Fim

Fim: Loops Decisões são importantes para escolher entre duas alternativas, e para iterar uma computação (loop). Usamos as mesmas instruções assembly para as duas situações. Tudo depende de onde colocamos o rótulo para o qual saltaremos. Exemplo de loop Se as variáveis g, h, i, j correspondem aos registradores $s1 a $s4, e o endereço base do vetor A (de 100 elementos) está em $s5, compile o seguinte trecho em C. Loop: g = g + A[i]; if ( (i = i + j)!= h ) goto Loop; Loop: add $t1, $s3, $s3 # $t1 = 2 * i add $t1, $t1, $t1 # $t1 = 4 * i add $t1, $t1, $s5 # $t1 = ender. de A[i] lw $t0, 0($t1) # $t0 = A[i] add $s1, $s1, $t0 # g = g + A[i] add $s3, $s3, $s4 # i = i + j bne $s3, $s2, Loop # Loop se (i!= h)

Compilando um laço do tipo while Temos inicialmente que carregar save[i] para um registrador temporário: Loop: add $t1, $s3, $s3 # $t1 = 2 * i add $t1, $t1, $t1 # $t1 = 4 * i add $t1, $t1, $s6 # $t1 = endereço de save[i] lw $t0, 0($t1) # $t0 = save[i] Agora fazemos o teste do loop, saindo se save[i]!= k bne $t0, $s5, Fim # vá para Fim se save[i]!= k add $s3, $s3, $s4 # i = i + j Devemos agora voltar para o while no inicio do loop Fim: j Loop # vá para Loop Comparando dois registradores Os testes de igualdade ou desigualdade são provavelmente os mais populares, mas às vezes queremos testar se uma variável é menor do que outra Por exemplo, um for pode querer testar se um índice é menor do que zero. Em MIPS, temos uma instrução que compara os valores de dois registradores, e atribui 1 a um terceiro registrador se o primeiro registrador é menor que o segundo, e 0 caso contrário: slt (set on less than) slt $t0, $s3, $s4 Se $s3 < $s4, $t0 recebe 1, caso contrário, recebe 0

Exemplo: desvio se menor que Qual o código para testar se a variável a, mapeada no registrador $s0, é menor que a variável b (registrador $s1), e desviar para o rótulo Menor se a condição for satisfeita? Primeiro usamos a instrução slt e um registrador temporário: slt $t0, $s0, $s1 # $t0 = (a < b)? Registrador $t0 é 1 se a < b. Portanto, testamos se $t0 não é 0: bne $t0, zero, Menor # vá para Menor se $t0!=0 # ou seja, se (a < b) Tradução de if (a < b) then... else if (i < j) f = g + h; else f = g h; slt $t0, $s0, $s1 # $t0 = (a < b)? beq $t0, zero, Else # vá para else se a >= b add $s0, $s1, $s2 # f = g + h (se i == j) j Fim # vá para Fim Else: sub $s0, $s1, $s2 # f = g h (se i!= j) Fim:

Um outro tipo de desvio incondicional Até agora vimos uma instrução de desvio incondicional, através da instrução j Rotulo # desvio para rótulo Nesta instrução, temos que especificar um rótulo, ou seja um endereço fixo, para o qual o Program Counter será desviado. Em diversas situações, pode ser interessante que desviemos para um endereço variável, armazenado em um registrador. Para tanto, existe a instrução jr: jr registrador endereço registrador #desvio para #contido no Comando switch A linguagem C define o comando switch, que permite que o programador selecione uma alternativa dentre várias, dependendo de um único valor. Como compilar o seguinte trecho de código? switch (k) { } case 0: f = i + j; break; case 1: f = g + h; break; case 2: f = g h; break; case 3: f = i j; break;

Comando switch Desta forma, se temos n casos possíveis, teremos, em média, que testar n/2 casos contra k para encontrar o caso desejado. Como podemos implementar o comando switch de maneira mais eficiente? Em alguns casos, podemos utilizar uma tabela de endereços, de tal forma que ao acessar a tabela na posição correspondente a k, TabEnd[k], obtenhamos o endereço do rótulo desejado. Desta maneira, podemos tomar a decisão em tempo constante. Comando switch Melhorando o comando switch: switch (k) { } case 0: f = i + j; break; case 1: f = g + h; break; case 2: f = g h; break; case 3: f = i j; break; Vamos supor que as variáveis f, g, h, i, j, k estão nos registradores $s0 a $s5, e que $t2 contenha o valor 4.

Vamos utilizar k para indexação; por isto temos que multiplicar k por 4. Comando switch add $t1, $s5, $s5 add $t1, $t1, $t1 # t1 = 2 * k # t1 = 4 * k Suponha que o vetor de endereços TabEnd, cujo endereço está em $t4, possui quatro posições, com os endereços correspondentes aos rótulos L0, L1, L2 e L3: add $t1, $t1, $t4 # t1 = endereço de TabEnd[k] lw $t0, 0($t1) # t0 = TabEnd[k] Agora saltamos para o endereço presente em $t0: jr $t0 Comando switch Por fim tratamos os casos do switch: L0: add $s0, $s3, $s4 # f = i+j j Fim L1: add $s0, $s1, $s2 # f = g + h j Fim L2: sub $s0, $s1, $s2 # f = g h j Fim L3: sub $s0, $s3, $s4 # f = i j (não precisamos saltar # para o Fim, já que a próxima # instrução tem o rótulo Fim) Fim:

Instruções de suporte a procedimentos Procedimentos ou subrotinas são utilizadas pelos programadores para: Facilitar a compreensão e manutenção de código; Possibilitar o reaproveitamento de código. O código de um procedimento fica isolado, sendo a interface com o restante do código dada pelos argumentos de entrada e pelos resultados de saída. Instruções de suporte a procedimentos Na execução de um procedimento, um programa deve seguir os seguintes passos: Colocar os argumentos (parâmetros) em um lugar em que o procedimento possa acessá-los; Transferir o controle para o procedimento; Executar a tarefa desejada; Colocar o resultado da execução em um lugar em que o código que chamou o procedimento possa acessar; Retornar o controle para o ponto de origem.

$ra: registrador utilizado para retornar para o ponto de origem (ra = return address). Instruções de suporte a procedimentos Preparo argumentos para o procedimento Desvio para procedimento $a0-$a3 Continuação do programa $ra; desvio Execução do procedimento Valores de retorno do procedimento Retorno para a continuação do programa $v0-$v1 jr $ra Exemplo de procedimento C... soma(a,b);... /* a:$s0; b:$s1 */ } int soma(int x, int y) { /* x:$a0; y:$a1 */ return x+y; } M I P S endereço 1000 add $a0,$s0,$zero # x = a 1004 add $a1,$s1,$zero # y = b 1008 addi $ra,$zero,1016 # $ra = 1016 1012 j soma # desvio para soma 1016... 2000 soma: add $v0,$a0,$a1 2004 jr $ra # volte p/ origem, # no endereço 1016

jal (jump and link)... Link, neste caso, quer dizer que é formada, no registrador $ra, uma referência para a instrução que vem logo após a instrução jal, ou seja, a instrução jal é equivalente ao seguinte código: $ra = PC + 4 j Rótulo Por que existe a instrução jal?? Procedimentos são muito comuns: faça o caso comum ser rápido Exemplo de procedimento (revisado) C M I P S... soma(a,b);... /* a:$s0; b:$s1 */ } int sum(int x, int y) { /* x:$a0; y:$a1 */ return x+y; } end 1000 add $a0,$s0,$zero # x = a 1004 add $a1,$s1,$zero # y = b 1008 jal soma # prepara $ra e # jump p/ proc soma 1012... 2000 soma: add $v0,$a0,$a1 2004 jr $ra # volte p/ origem, # no endereço 1012

jr $ra # retorne para origem Usando mais registradores Suponha que um procedimento necessite de mais registradores do que os 4 registradores de argumentos e os 2 de retorno. O código que vai chamar este procedimento pode estar utilizando diversos registradores, de tal forma que o procedimento não pode utilizar os registradores de qualquer forma, uma vez que valores importantes poderiam ser perdidos. Assim, qualquer registrador que o procedimento utilize e que possa ser do interesse do código chamador deve ter seu valor restaurado para o valor anterior à execução do procedimento. Usando mais registradores Como fazer isto? Processo conhecido por register spilling: Uso de uma pilha, estrutura de dados do tipo LIFO (last-in first-out); Temos um apontador para o topo da pilha; Este apontador é ajustado em uma palavra para cada registrador que é colocado na pilha (operação conhecida por push), ou retirado da pilha (operação conhecida por pop). Em MIPS, um registrador é utilizado somente para indicar o topo da pilha: sp (stack pointer)

Alocação de memória em C Endereço $sp stack pointer 0 Pilha Heap Estático Código Espaço para procedimentos armazenarem informações Espaço explicitamente criado malloc: apontadores em C Variáveis declaradas uma vez para todo programa Programa Exemplo: exemplo_proc Suponha que tenhamos o seguinte código: int exemplo_proc (int g, int j, int i, int h) { int f; f = (g+h) (i+j); return f; } Vamos gerar o código correspondente em assembly MIPS.

sw $s0, 0(sp) # empilha $s0 Exemplo : exemplo_proc Como ficou a pilha? $sp Valores empilhados antes da função $sp Valores empilhados antes da função $t1 $t0 $s0 Pilha antes da função Pilha durante execução da função Exemplo : exemplo_proc As próximas instruções correspondem ao corpo do procedimento: add $t0, $a0, $a1 add $t1, $a2, $a3 sub $s0, $t0, $t1 # $t0 = g + h # $t1 = i + j # f = $t0 = (g+h) (i+j) O resultado deve ser armazenado no registrador $v0: add $v0, $s0, $zero # retorna f em $v0

# este procedimento Instruções de suporte a procedimentos No exemplo anterior, nós utilizamos registradores temporários e assumimos que os valores deles deveriam ser guardados e restaurados. Para evitar que registradores que não são utilizados sejam empilhados e desempilhados, MIPS oferece duas classes de registradores: $t0-$t9: 10 registradores temporários que não são preservados pela função que é chamada. $s0-$s7: 8 registradores que têm seus valores preservados no processo de chamada de procedimento. Desta forma, se estes registradores forem utilizados pelo procedimento, eles devem ter seus valores empilhados no início do procedimento e desempilhados no final. Instruções de suporte a procedimentos Esta simples convenção faz com que percamos menos tempo empilhando e desempilhando registradores No exemplo, teríamos que preservar somente o valor de $s0. O que fazer se temos um código que utiliza registradores temporários e vai chamar uma função?? Podem aparecer problemas quando fazemos funções que chamam outras funções (por exemplo, funções recursivas)??

Como resolver? Procedimentos aninhados: convenção sobre registradores Uma solução é empilhar todos os registradores que precisam ser preservados. Para isto, temos que estabelecer uma convenção entre a subrotina que chama a função e a subrotina chamada, para estabelecer quais registradores serão preservados, e por quem. Definições Chamadora: função que faz a chamada, utilizando jal; Chamada: função sendo chamada. Podemos pensar nestas convenções como sendo um contrato entre a Chamadora e a Chamada; Por que utilizar convenções para chamadas de procedimentos? Se tanto a sub-rotina Chamadora quanto a Chamada obedecerem a convenção, temos os seguintes benefícios: programadores podem escrever funções que funcionam juntas; Funções que chamam outras funções como as recursivas funcionam corretamente. Atenção!! Chamadora ou chamada não representa uma propriedade da função, mas sim o papel que a função exerce em uma chamada de procedimento específica. Assim, uma função pode exercer o papel de chamadora e de chamada, só que em diferentes chamadas de procedimento.

Registradores temporários $t0 - $t9 Direitos da Chamadora e da Chamada Direitos da Chamadora Utilizar os registradores s, sem que eles sejam alterados pela Chamada Assumir que os valores de retorno e a pilha estão corretos Registradores que devem ser preservados pela Chamada: Registradores $s $s0 - $s7 Exemplo: soma_recursiva Suponha que tenhamos o seguinte código, que calcula a soma n + (n-1) + + 2 + 1 de forma recursiva: int soma_recursiva (int n) { if (n < 1) return 0; else return n + soma_recursiva(n-1) } Vamos gerar o código correspondente em assembly MIPS.

que é armazenado corresponde a um endereço que está na sub-rotina que chama esta função Exemplo: soma_recursiva Vamos agora compilar o corpo da função. Inicialmente, testamos se n < 1: slti $t0, $a0, 1 # testa se n < 1 beq $t0, $zero, L1 # se n>=1, vá para L1 Se n >=1, a função deve retornar o valor 0. Não podemos nos esquecer de restaurar a pilha. add $v0, $zero, $zero # valor de retorno é 0 add $sp, $sp, 8 # remove 2 itens da pilha jr $ra # retorne para depois de jal Por que não carregamos os valores de $a0 e $ra antes de ajustar $sp?? L1: Exemplo: soma_recursiva Se n >=1, decrementamos n e chamamos novamente a função soma_recursiva com o novo valor de n. subi $a0, $a0, 1 jal soma_recursiva # argumento passa a ser (n-1) # calcula a soma para (n-1) Quando a soma para (n-1) é calculada, o programa volta a executar na próxima instrução. Restauramos o endereço de retorno e o argumento anteriores, e incrementamos o apontador de topo de pilha: lw $a0, 0($sp) lw $ra, 4($sp) addi $sp, $sp, 8 # restaura o valor de n # restaura o endereço de retorno # retira 2 itens da pilha.

Variáveis automáticas e estáticas Variáveis automáticas são aquelas que são locais a um procedimento, e que portanto só são acessíveis enquanto o procedimento está ativado. Quando uma função contém dados que são grandes demais para serem colocados em registradores, eles são colocados na pilha. Exemplos são estruturas de dados como registros (structs) ou vetores estáticos (que não usam malloc para alocação). Já as variáveis estáticas são aquelas que estão ativas durante toda a execução do programa. Em C, elas são as variáveis globais e as variáveis marcadas como static. Variáveis automáticas e estáticas A região da pilha onde são armazenados os registradores salvos e as variáveis locais a um procedimento é chamada de registro de ativação. Para facilitar o acesso a estas variáveis, o registrador $fp (frame pointer) é utilizado. Ele marca o início do registro de ativação, e não é atualizado de forma automática, só sendo utilizado se houver variáveis do procedimento na pilha. A vantagem de se usar $fp é que $sp pode ter o valor alterado durante a execução da função. Para facilitar o acesso às variáveis estáticas, o registrador $gp (global pointer) é utilizado. Estas variáveis são alocadas na região de memória dados estáticos, ver figura 3.22 do livro (seção 3.9)

Pilha antes da função Pilha durante execução da função Registradores MIPS: resumo da convenção de software 0 zero constante 0 1 at reservado para o montador 2 v0 resultados de funções v1 4 a0 argumentos 5 a1 6 a2 7 a3 8 t0 temporários: Chamadora... é que deve salvar 15 t7 16 s0 Chamada é que deve... salvar 23 s7 24 t8 temporários (cont.) 25 t9 26 k0 reservados para o kernel 27 k1 do sistema operacional 28 gp global pointer 29 sp stack pointer 30 fp frame pointer 31 ra endereço de retorno Fig. A.1 0 Resumo: instruções vistas até agora Categoria Instrução Exemplo Semântica Aritmética Adição add $s1, $s2, $s3 $s1 = $s2 + $s3 Subtração sub $s1, $s2, $s3 $s1 = $s2 - $s3 Transferência Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100] de dados Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1 Desvio Branch on beq $s1, $s2, L If ($s1 == $s2) goto L Condicional equal Branch on not equal bne $s1, $s2, L If ($s1!= $s2) goto L Set on less slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0; than Desvio Jump j 2500 goto 10000 Incondicional Jump register jr $t1 goto $t1 Jump & link jal 2500 $ra = PC + 4; goto 10000

Trabalhando com caracteres e strings No entanto, como grande parte dos programas utiliza texto, MIPS fornece instruções específicas para mover bytes. Leitura de bytes: load byte (lb): lê um byte da memória, colocando-o nos 8 bits mais à direita d e um registrador Ex: lb $t0, 0($sp) # lê byte que está n o topo da pilh a Trabalhando com caracteres e strings Escrita de bytes: store byte (sb): escreve na memória o byte que está nos 8 bits mais à direita de um registrador Ex: sb $t0, 0($sp) # escreve byte no topo da pilha

ocupa um byte? Trabalhando com caracteres e strings A string é armazenada em uma estrutura, em que uma variável diz o comprimento da cadeia e outra traz os caracteres que compõe a cadeia; Ex: casa = {4, _c a s a_} = {4, 99 97 115 97} A última posição de uma cadeia é indicada por um caractere especial. Ex: casa = _c a s a_ 0 = 99 97 115 97 0 Esta é a representação utilizada pela linguagem C, sendo o marcador de final de string o valor 0. Exemplo: string_copy Suponha que tenhamos o seguinte código: void strcpy(char x[], char y[]) { int i; i = 0; while ((x[i] = y[i])!= 0) i = i + 1; } Vamos gerar o código correspondente em assembly MIPS.

Exemplo: string_copy Podemos fazer o laço então; o endereço de y[i] é formado adicionando i com o endereço base de y: L1: add $t1, $a1, $s0 # $t1 = endereço de y[i] Por que não multiplicamos i por 4?? y é um vetor de bytes, e não de palavras!!! Agora carregamos o caractere y[i]: lb $t2, 0($t1) # $t2 = y[i] Exemplo: string_copy Preparamos agora o endereço de x[i] em $t3, e armazenamos y[i]: add $t3, $a0, $s0 # $t3 = endereço de x[i] sb $t2, 0($t3)# x[i] = y[i] Se o caractere y[i] for 0, saimos do loop: beq $t2, $zero, L2 # se y[i] == 0, desvie para L2 Senão, incremente i e itere novamante: addi $s0, $s0, 1 # $s0 = $s0 + 1 j L1 # desvie para L1

que $s em funções que não chamam outra função. Padrão Unicode Está se tornando cada vez mais comum um outro tipo de codificação para caracteres, o padrão Unicode. Java utiliza este padrão. Este padrão, além de representar todos os caracteres latinos, pode representar símbolos de letras orientais, dentre outros. Um caractere passa a ocupar 16 bits. Quantos símbolos podem ser representados? MIPS inclui instruções para trabalhar com 16 bits, mas não veremos elas neste curso. Quem estiver curioso, consulte o apêndice A do livro.