SlideShare uma empresa Scribd logo
1 de 125
Baixar para ler offline
Conjunto de Instruções
Para comandar o hardware do
computador, é necessário que falemos
sua língua:
♦ As palavras da linguagem de máquina são
chamadas de instruções;
♦ O vocabulário forma o conjunto de instruções, que
determina a interface hardware/software.
♦ Computadores com arquiteturas diferentes
implementam conjuntos de instruções distintos
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
Implementação de Programas
♦ Um Programa de Computador é, basicamente, uma
seqüência de comandos ou instruções representando um
algoritmo a ser executado pela máquina.
♦ Normalmente os programadores usam Linguagens de
Programação de Alto Nível (LAN), como Pascal e C
♦ Entretanto, estas linguagens correspondem a um nível de
abstração elevado.
♦ As linguagens dos processadores como o x86 ou o MIPS
correspondem a um nível de abstração baixo, e são
denominadas de Linguagens de Baixo Nível (LBN)
♦ 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
Níveis de Abstração
♦ Diferentes Níveis de Abstração
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
Tradutores
♦ Montadores, Compiladores e Interpretadores:
montado
r
execuçã
o
Código fonte
(ling.
Montagem)
Código
Objeto
compilado
r
Código
fonte
(LAN)
Código
Objeto
execuçã
o
Interpretador
Código
fonte
(LAN ou
Assembl
y)
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
R0 - R31
PC
HI
LO
OP
OP
OP
rs rt rd sa funct
rs rt imediato
Destino do jump
Registradores
add a, b, c # a = b + c
add a, a, d # a = b + c + d
add a, a, e # a = b + c + d +
e
Operações do Hardware
Todo computador deve ser capaz de realizar
operações aritméticas.
Instruções aritméticas no MIPS têm formato fixo,
realizando somente uma operação e tendo três
“variáveis”
Somente uma
instrução por
linha
Comentários
ex: a = b + c + d + e
ex..: add a,b,c a = b
+ c
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 variá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?
add a, b, c #a = b + c
sub d, a, e #d = a - e
a = b + c;
d = a – e;
Exemplo 2
♦ Qual o código gerado por um compilador C
para o seguinte trecho?
add t0, g, h # temporário t0 = g
+ h
add t1, i, j # temporário t1 = i +
j
sub f, t0, t1 #f = (g + h) – (I + j)
Somente uma operação é feita por instrução: necessidade
de variáveis temporárias.
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 variá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
♦ Registradores do MIPS são de 32 bits;
♦ no MIPS, blocos de 32 bits são chamados de
palavra;
♦ Número de registradores é limitado: MIPS → 32
registradores, numerados de 0 a 31
♦ Princípio #2 para projetos: menor é mais rápido
♦ Um número muito grande de registradores
aumentaria o período de clock.
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?
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)
As variáveis f, g, h, i, j podem ser mapeadas nos
registradores $s0, $s1, $s2, $s3 e $s4, respectivamente.
f = (g + h) – (i + j)
Operandos e Registradores
♦ Estruturas de dados podem ser bastante complexas,
com um número de elementos grande demais para
serem armazenados nos registradores
♦ Estruturas de dados, como vetores, são armazenadas na
memória
♦ Operações aritméticas em MIPS só podem ser feitas
quando os operandos estão em registradores
♦ MIPS deve fornecer instruções para transferir dados entre
a memória e os registradores
♦ Para acessar uma palavra na memória, a instrução deve
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
DadosEndereços
Memória
Transferindo dados da
memória
♦ A instrução de transferência de dados da memória
para o registrador é chamada de load.
♦ Formato:
♦ Endereço de memória acessado é dado pela soma
da constante (chamada de offset) com o conteúdo
do registrador base
Em MIPS, o nome da instrução é:
lw (load word)
lw registrador destino, constante (registrador
base)
Exemplo
♦ Suponha que temos um apontador a para um inteiro que está na
memória, com o endereço de memória dado no registrador $s3
(ou seja, o apontador a está em $s3). Suponha ainda que o
compilador associou as variáveis g e h aos registradores $s1 e
$s2. Qual o código para o seguinte trecho em C?
♦ Primeiro temos que pegar o operando que está na memória e
transferi-lo para um registrador:
g = h + *a;
lw $t0, 0($s3) # temporário t0 =
*a
add $s1, $s2, $t0 # g = h + *a
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].
Vetor na memória
Dados
Endereços
Endereço base
de A
... 5 10 0 0 0 0 15 42 ...
... 100 101 102 103 104 105 106 107 ...
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
♦ Primeiro temos que pegar o operando que está na
memória e transferi-lo para um registrador:
g = h + A[8]
?
lw $t0, 8($s3) # temporário t0 =
A[8]
add $s1, $s2, $t0 # g = h + A[8]
100
10
101
1
Acesso à memória
♦ Uma vez que bytes (8 bits) são úteis em muitos
programas, a maior parte das arquiteturas permite acessar
bytes;
♦ Portanto, o endereço de uma palavra é o endereço de um
dos 4 bytes dentro da palavra
♦ Assim, endereços de palavras consecutivas diferem em 4
12
8
4
0
Processador
DadosEndereços
Memória
Cada posição do vetor (de inteiros) é uma palavra, e portanto
ocupa 4 bytes
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].
Vetor na memória (2)
Dados
Endereços
Endereço base
de A
... 5 10 0 0 0 0 15 4 2 ...
... 400 404 408 412 416 420 424 428 ...
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 nona posição do vetor A, A[8], está no offset 8 x 4 = 32
♦ A décima-terceira posição do vetor A, A[12], está no offset
12 x 4 = 48
A[12] = h + A[8] ?
lw $t0, 32($s3) # temporário t0 = A[8]
add $t0, $s2, $t0 # temporário t0 = h + A[8]
lw $t0, 48($s3) # carrega A[12] em $t0!!!!
Transferindo dados para a
memória
♦ A instrução de transferência de dados de um registrador
para a memória é chamada de store.
♦ Formato:
♦ Endereço de memória acessado é dado pela soma da
constante (chamada de offset) com o conteúdo do
registrador base
No MIPS, o nome da instrução é:
sw (store word)
sw registrador fonte, constante (registrador
base)
Endereço absoluto de A[3]
♦ Para obter o endereço absoluto precisamos:
0 1 2 3 4 5 6 7 8 9
...
Registrador base
exe. $s2
Variável i ( i = 3) ($s4)
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:
♦ 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:
g = h + A[i] ?
add $t1, $s4, $s4 # $t1 = 2 * i
add $t1, $t1, $t1 # $t1 = 4 * i
Variável de índice
♦ Para pegarmos A[i], somamos $t1 com o endereço base
de A, dado em $s3:
♦ Agora podemos carregar A[i] para um registrador
temporário, e realizar a soma, que será armazenada em g:
add $t1, $t1, $s3 # $t1 = endereço de
A[i]
lw $t0, 0($t1) # temporário $t0 = A[i]
add $s1, $s2, $t0 # g = h + A[i]
Resumo da Tradução
g = h + A[i]
add $t1, $s4, $s4 # $t1 = 2 * i
add $t1, $t1, $t1 # $t1 = 4 * i
add $t1, $t1, $s3 # $t1 = endereço de A[i]
lw $t0, 0($t1) # temporário $t0 = A[i]
add $s1, $s2, $t0 # 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]
?
Solução
add $t0, $s0, $s0 # $t0 = 2*i
add $t0, $t0, $t0 # $t0 = 4*i
add $t0, $t0, $s2 # $t0 = endereço de A[i]
lw $t1, 0($t0) # $t1 = A[i]
add $t1, $s1, $t1 # $t1 = g + A[i]
lw $t0, 0($s2) # $t0 = A[0]
sub $t1, $t1, $t0 # $t1 = g + A[i] – A[0]
add $t0, $s0, $s1 # $t0 = i + g
add $t0, $t0, $t0 # $t0 = 2 * (i + g)
add $t0, $t0, $t0 # $t0 = 4 * (i + g)
add $t0, $t0, $s2 # $t0 = endereço de A[i + g]
sw $t1, 0($t0) # A[i + g] = g + A[i] – A[0]
A[i+g] = g + A[i] – A[0]?
i,g,A = $s0, $s1,$s2
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.
♦ MIPS requer que todas as palavras comecem em
endereços que são múltiplos de 4 bytes
♦ Chamamos isto de alinhamento: objetos têm que ter
endereços que sejam múltiplos de seus tamanhos.
0 1 2 3
Alinhado
Não alinhado
Alinhamento de Dados na
Memória
♦ Processadores podem numerar bytes dentro de uma
palavra, de tal forma que o byte com o menor
número é o mais a esquerda ou o mais a direita. Isto
é chamado de byte order.
♦ Ex: .byte 0, 1, 2, 3
♦ Big endian: IBM 360/370, Motorola 68k, MIPS, Sparc, HP
PA
♦ Little Endian: Intel 80x86, MIPS, DEC Vax, DEC Alpha
3 2 1 0little endian
0 1 2 3big
endian
Ordenamento dos Bytes
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
♦ 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.
0 17 18 8 0 32
add $t0, $s1, $s2
♦ Também podemos representar tais números em binário:
♦ Para distinguir da linguagem Assembly, chamamos esta
representação numérica de linguagem de máquina, e a
seqüência de tais instruções de código de máquina
♦ Chamamos esta estrutura de campos de formato da
instrução. Todas as instruções em MIPS possuem 32 bits
(“simplicidade favorece regularidade”).
000000 10001 10010 01000 00000
100000
Representando Instruções no
Computador ...
6 bits 5 bits 5 bits 5 bits 5 bits 6
bits
Formato Instrução MIPS
♦ Daremos nomes aos campos para simplificar a discussão:
♦ 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
op rs rt rd shamt
funct6 bits 5 bits 5 bits 5 bits 5 bits 6
bits
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.
Formato Instrução MIPS...
♦ Princípio #3 para projetos: bons projetos
demandam bons compromissos.
♦ Compromisso escolhido pelos projetistas do MIPS: manter
todas as instruções com o mesmo tamanho → diferentes
tipos de instrução podem ter diferentes formatos de
instrução.
♦ O formato mostrado anteriormente é chamado de tipo-R
(de registrador) ou formato-R
♦ Um segundo formato é usado pelas instruções de
transferência de dados: tipo-I ou formato-I
Formato Instrução MIPS...
♦ Formato tipo-I:
♦ 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.
op rs rt endereço
6 bits 5 bits 5 bits 16 bits
Formato Instrução MIPS...
♦ Ex: lw $t0, 32($s3) #temporário $t0 = A[8]
♦ 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).
op rs rt endereço
Formato Instrução MIPS...
♦ Primeiro campo determina tipo do formato.
♦ Pergunta: por que não utilizar somente um campo
de 12 bits para os campos op e funct nas
instruções de tipo-R??
Codificação das Instruções
Vistas
Instruçã
o
Formato op rs rt rd shamt func
t
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 é:
♦ Qual o código de máquina destas 3 instruções?
lw $t0,1200($t1) # $t0 = A[300]
add $t0, $s2, $t0 # $t0 = h + A[300]
sw$t0, 1200($t1) # A[300] = h +
A[300]
Exemplo de compilação manual
...
lw $t0,1200($t1)
add $t0, $s2, $t0
sw $t0, 1200($t1)
op rs r
t
r
d
Endereço /
shamt
func
t
35 9 8 1200
0 1
8
8 8 0 32
43 9 8 1200
op rs rt rd Endereço /
shamt
funct
100011 01001 01000 0000 0100 1011 0000
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)
Instruções de decisão no MIPS
♦ em C, isto seria equivalente a:
beq registr1, registr2,
L1
Semântica: “Desvie se (valores nos registradores são) iguais”
if (registr1== registr2) goto
L1
Instruções de decisão no MIPS ...
♦ em C, isto seria equivalente a:
♦ Estas instruções são chamadas de desvios condicionais.
bne registr1, registr2, L1
Semântica: “desvie se (valores nos registradores) não
são iguais”
if (registr1!=registr2) goto L1
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?
Como instruções são armazenadas na memória, elas têm
endereços também!!
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)
Instruções para tomada de
decisões
♦ O montador (assembler) faz com que o compilador ou o
programador em linguagem assembly não tenha que ficar
calculando endereços para os desvios (branches) ao
permitir o uso de rótulos.
♦ Compiladores criam desvios e rótulos sem que o
programador de linguagens de alto nível os tenha que
especificar. Esta é uma das razões de por que é mais
rápido programar em linguagens de alto nível.
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:
• 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.
j rotulo
beq $0,$0,rotulo
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?
♦ Queremos implementar o seguinte fluxo:
Fim
i == j?
f=g+h f=g-h
(falso)
i != j
(verdadeiro)
i == j
if (i == j) f = g + h; else f = g – h;
Exemplo: código para if
Primeiro devemos saber se i é igual a j:
Se i == j, somamos g com h e armazenamos em f
Precisamos agora ir para o fim do if. Para isto
utilizamos um desvio incondicional ( j, de jump ):
Tratamos agora o caso em que i != j
bne $s3, $s4, else # vá para else se i !=
j
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:
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
♦ Programadores normalmente não utilizam comandos goto
(o uso deles não é considerado boa prática de
programação!!!). Assim, o compilador tem que traduzir os
loops tradicionais em linguagem MIPS.
♦ Exemplo em C:
♦ Se i, j, e k correspondem aos registradores $s3, $s4 e
$s5, e o endereço base do vetor save está no registrador
$s6, qual o código assembly MIPS correspondente?
while (save[j] == k)
i = i + j;
♦ Temos inicialmente que carregar save[i] para um
registrador temporário:
♦ Agora fazemos o teste do loop, saindo se save[i] != k
♦ Devemos agora voltar para o while no inicio do loop
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]
bne $t0, $s5, Fim # vá para Fim se save[i] != k
add $s3, $s3, $s4 # i = i + j
j Loop # vá para Loop
Fim:
Compilando um laço do tipo while
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:
♦ → Se $s3 < $s4, $t0 recebe 1, caso contrário, recebe 0
slt (set on less than)
slt $t0, $s3, $s4
Compilando um laço do tipo while
♦ Compiladores MIPS utilizam as instruções slt, beq, bne, e
o valor fixo zero para criar todas as relações de
comparação: igual, diferente, menor que, menor ou igual
a, maior que e maior ou igual a.
♦ O registrador $zero (registrador 0) tem seu valor fixado
em zero.
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:
♦ Registrador $t0 é 1 se a < b. Portanto, testamos se $t0 não é 0:
slt $t0, $s0, $s1 # $t0 = (a <
b)?
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:
Por que não utilizar uma instrução
só para “desvie se menor que”?
♦ MIPS não tem instruções como “desvie se menor que”
porque elas são complicadas demais: ou elas
aumentariam o período de clock ou seriam necessários
mais ciclos de clock por instrução (além de fazer com que
fosse necessário mais hardware).
♦ As duas instruções slt e bne são mais rápidas e mais
úteis, pois também servem para outros propósitos.
Um outro tipo de desvio
incondicional
♦ Até agora vimos uma instrução de desvio incondicional,
através da instrução
♦ 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:
j Rotulo # desvio para rótulo
jr registrador #desvio para endereço
#contido no
registrador
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
♦ Uma forma de se fazer isto é tratar o comando switch
como sendo uma seqüência de comandos if:
if ( (k >= 0) && (k <= 3))
if (k == 0) f = i + j;
else if (k == 1) f = g + h;
else if (k == 2) f = g - h;
else f = i + j;
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:
♦
♦ 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.
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
♦ Utilizaremos a variável k como índice na tabela de
endereços, e desviar de acordo com o valor carregado;
♦ Primeiro, temos que verificar se 0 <= k <= 3:
♦ Vamos utilizar k para indexação; por isto temos que
multiplicar k por 4.
slt $t3, $s5, $zero # teste se k<0
bne $t0, $zero, Fim # se k<0, vá para Fim
slt $t3, $s5, $t2 # teste se k<4
beq $t3, $zero, Fim # se k>=4 vá para Fim
Comando switch
♦ 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:
♦ Agora saltamos para o endereço presente em $t0:
add $t1, $s5, $s5 # t1 = 2 * k
add $t1, $t1, $t1 # t1 = 4 * k
add $t1, $t1, $t4 # t1 = endereço de
TabEnd[k]
lw $t0, 0($t1) # t0 = TabEnd[k]
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:
Resumo: instruções vistas até
agora
Categoria Instrução Exemplo Semântica
Adição add $s1, $s2, $s3 $s1 = $s2 + $s3Aritmética
Subtração sub $s1, $s2, $s3 $s1 = $s2 - $s3
Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100]Transferência
de dados Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1
Branch on
equal
beq $s1, $s2, L If ($s1 == $s2) goto L
Branch on not
equal
bne $s1, $s2, L If ($s1 != $s2) goto L
Desvio
Condicional
Set on less
than
slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0;
Jump j 2500 goto 10000Desvio
Incondicional Jump register jr $t1 goto $t1
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.
Instruções de suporte a
procedimentos
♦ Qual o lugar mais rápido que pode armazenar dados em
um computador?
♦ Registrador. Queremos utilizar registradores
sempre que possível
♦ Software para MIPS aloca os seguintes registradores
para chamada de procedimentos:
♦ $a0 - $a3: quatro registradores que são utilizados para
passar parâmetros para os procedimentos;
♦ $v0 - $v1: dois registradores que são utilizados para
retornar valores calculados pelos procedimentos
(resultados)
♦ $ra: registrador utilizado para retornar para o ponto de
origem (ra = return address).
Valores de retorno do procedimento
Desvio para procedimento
Instruções de suporte a
procedimentos
Execução do procedimento
Preparo argumentos para o procedimento $a0-$a3
$ra;
desvi
o
$v0-$v1
Continuação do programa
Retorno para a continuação do programa jr $ra
C
M
I
P
S
Exemplo de procedimento
... soma(a,b);... /* a:$s0; b:$s1 */
}
int soma(int x, int y) { /* x:$a0; y:$a1 */
return x+y;
}
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)
♦ O assembly MIPS tem uma instrução de desvio só para
procedimentos; ela causa um desvio para um endereço,
e ao mesmo tempo guarda o endereço da próxima
instrução em $ra: jal (jump and link).
jal Rotulo #desvio para o procedimento que tem
#endereço marcado por Rótulo
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:
♦ Por que existe a instrução jal??
Procedimentos são muito comuns:
“faça o caso comum ser rápido”
$ra = PC + 4
j Rótulo
C
M
I
P
S
Exemplo de procedimento
(revisado)
... 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
Exercício
♦ Qual o código em assembly para MIPS equivalente ao
seguinte trecho em C, estando i em $s0 e j em $s1?
• i = sub(i,j); /* sub(a, b) {return a-b;} */
...
add $a0, $s0, $zero # a0 = i
add $a1, $s1, $zero # a1 = j
jal sub # chama a função
sub
add $s0, $v0, $zero # i = sub(i,j)
...
sub: sub $v0, $a0, $a1 # $v0 = a – b
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)
Usando mais registradores
♦ Por razões históricas, a pilha “cresce” do maior endereço
para o menor endereço:
♦ Para colocar um valor na pilha (push), devemos
decrementar $sp em uma palavra e mover o valor
desejado para a posição de memória apontada por $sp;
♦ Para retirar um valor da pilha (pop), devemos ler este
valor da posição de memória apontado por $sp, e então
incrementar $sp em uma palavra.
0
Endereço
Código Programa
Estático Variáveis declaradas uma
vez para todo programa
Heap
Espaço explicitamente criado
malloc: apontadores em C
Pilha
Espaço para procedimentos
armazenarem informações$sp
stack
pointer
Alocação de memória em C
Exemplo: exemplo_proc
♦ Suponha que tenhamos o seguinte código:
Vamos gerar o código correspondente em assembly MIPS.
int exemplo_proc (int g, int j, int i, int h)
{
int f;
f = (g+h) – (i+j);
return f;
}
Exemplo : exemplo_proc
♦ Os argumentos g, h ,i e j correspondem aos registradores
$a0, $a1, $a2 e $a3, e f corresponde a $s0. Primeiro
colocamos o rótulo do procedimento:
♦ exemplo_proc:
♦ Devemos então armazenar os registradores que serão
utilizados pelo procedimento. Como estaremos utilizando
os registradores $t0, $t1 e $s0, vamos colocá-los na pilha:
subi $sp, $sp, 12 # cria espaço para mais 3 itens na
pilha
sw $t1, 8(sp) # empilha $t1
sw $t2, 4(sp) # empilha $t2
sw $s0, 0(sp) # empilha $s0
Exemplo : exemplo_proc
Como ficou a pilha?
$sp
Valores
empilhados
antes da
função
Pilha antes
da função
$t1
$t0
$s0$sp
Valores
empilhados
antes da
função
Pilha durante
execução da função
Exemplo : exemplo_proc
♦ As próximas instruções correspondem ao corpo do
procedimento:
♦ O resultado deve ser armazenado no registrador $v0:
add $v0, $s0, $zero # retorna f em $v0
add $t0, $a0, $a1 # $t0 = g + h
add $t1, $a2, $a3 # $t1 = i + j
sub $s0, $t0, $t1 # f = $t0 = (g+h) –
(i+j)
Exemplo : exemplo_proc
♦ Antes de sair do procedimento, devemos restaurar os
valores de $t0, $t1 e $s0, retirando-os da pilha:
♦ Voltamos então para a instrução seguinte ao ponto em
que a função exemplo_proc foi chamada:
lw $s0, 0($sp) # desempilha $s0
lw $t0, 4($sp) # desempilha $t0
lw $t1, 8 ($sp) # desempilha $t1
addi $sp, $sp, 12 # remove 3 itens da pilha
jr $ra # retorna para a subrotina que
chamou
# 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)??
Procedimentos aninhados
♦ Suponha que o programa principal chama a função A com
o argumento 3, ao colocar 3 em $a0 e utilizar a instrução
jal A.
♦ Suponha ainda que a função A chama a função B com
argumento 7, ao colocar 7 em $a0 e utilizar a instrução
jal B.
♦ Uma vez que A ainda não terminou de executar temos um
conflito no uso do registrador $a0.
♦ Um problema também aparece para o registrador $ra, que
agora contém o valor para retorno de B.
♦ Se não tomarmos cuidado, poderemos não ser capazes
nem de voltarmos para o programa principal!!!
♦ 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.
Direitos da Chamadora e da
Chamada
♦ Direitos da Chamada:
• Utilizar livremente os registradores v, a, e t;
• Assumir que os argumentos são passados
corretamente.
♦ Registradores que devem ser preservados pela
Chamadora:
• Endereço de retorno $ra
• Argumentos $a0, $a1, $a2, $a3
• Valores de retorno $v0, $v1
• Registradores temporários $t0 - $t9
♦ 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
Direitos da Chamadora e da
Chamada
Exemplo: soma_recursiva
♦ Suponha que tenhamos o seguinte código, que calcula a
soma n + (n-1) + … + 2 + 1 de forma recursiva:
♦ Vamos gerar o código correspondente em assembly
MIPS.
int soma_recursiva (int n)
{
if (n < 1)
return 0;
else
return n + soma_recursiva(n-
1)
}
Exemplo: soma_recursiva
♦ O parâmetro n corresponde ao registrador $a0.
♦ Devemos inicialmente colocar um rótulo para a função, e
salvar o endereço de retorno, armazenado em $ra, o
parâmetro $a0:
♦ Na primeira vez que soma_recursiva é chamada, o valor
de $ra que é armazenado corresponde a um endereço
que está na sub-rotina que chama esta função
Soma_recursiva:
subi $sp, $sp, 8 # prepara a pilha para receber
2
# valores
sw $ra, 4($sp) # empilha $ra
sw $a0, 0($sp) # empilha $a0
Exemplo: soma_recursiva
♦ Vamos agora compilar o corpo da função. Inicialmente,
testamos se n < 1:
♦ Se n >=1, a função deve retornar o valor 0. Não podemos
nos esquecer de restaurar a pilha.
♦ Por que não carregamos os valores de $a0 e $ra antes de
ajustar $sp??
slti $t0, $a0, 1 # testa se n < 1
beq $t0, $zero, L1 # se n>=1, vá para
L1
add $v0, $zero, $zero # valor de retorno é 0
add $sp, $sp, 8 # remove 2 itens da pilha
jr $ra # retorne para depois de
jal
Exemplo: soma_recursiva
♦ Se n >=1, decrementamos n e chamamos novamente a
função soma_recursiva com o novo valor de n.
♦ 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:
L1:
subi $a0, $a0, 1 # argumento passa a ser (n-1)
jal soma_recursiva # calcula a soma para (n-1)
lw $a0, 0($sp) # restaura o valor de n
lw $ra, 4($sp) # restaura o endereço de retorno
addi $sp, $sp, 8 # retira 2 itens da pilha.
Exemplo: soma_recursiva
♦ Agora o registrador $v0 recebe a soma do argumento
antigo $a0 com o valor atual em $v0 (soma_recursiva
para n-1):
♦ Por último, voltamos para a instrução seguinte à que
chamou o procedimento:
add $v0, $a0, $v0 # retorne n +
soma_recursiva(n-1)
jr $ra # retorne para o
“chamador”
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)
Exemplo : $fp e $sp
$sp
Registro de
ativação
anterior (outra
função)
Pilha antes
da função
$fp
Registradores
$s salvos (se
houver)
Registradores
$a salvos (se
houver)
$ra salvo
Estruturas e
vetores locais
(se houver)$sp
Registro de
ativação
anterior (outra
função)
Pilha durante
execução da função
$fp
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
Registradores MIPS: resumo da
convenção de software
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.10
Categoria Instrução Exemplo Semântica
Adição add $s1, $s2, $s3 $s1 = $s2 + $s3Aritmética
Subtração sub $s1, $s2, $s3 $s1 = $s2 - $s3
Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100]Transferência
de dados Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1
Branch on
equal
beq $s1, $s2, L If ($s1 == $s2) goto L
Branch on not
equal
bne $s1, $s2, L If ($s1 != $s2) goto L
Desvio
Condicional
Set on less
than
slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0;
Jump j 2500 goto 10000
Jump register jr $t1 goto $t1
Desvio
Incondicional
Jump & link jal 2500 $ra = PC + 4; goto 10000
Resumo: instruções vistas até agora
Trabalhando com caracteres e
strings
♦ A maior parte dos computadores hoje utiliza bytes para
representar caracteres, quase sempre utilizando a
codificação ASCII (American Standard Code for
Information Interchange).
♦ A seção 3.7 traz a tabela ASCII, que deve ser utilizada
como fonte de consulta.
♦ Instruções de transferência de palavras (lw, sw) são
suficientes para transferir bytes também. Neste caso,
bytes individuais poderiam ser lidos ou escritos, através do
uso de máscaras, com as instruções e e ou lógicos (serão
vistas no capítulo 4).
t No entanto, como grande parte dos programas
utiliza texto, MIPS fornece instruções específicas
para mover bytes.
t Leitura de bytes:
t load byte (lb): lê um byte da memória,
colocando-o nos 8 bits mais à direita de um
registrador
t Ex: lb $t0, 0($sp) # lê byte que está no topo da
pilha
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
Trabalhando com caracteres e
strings
♦ Caracteres são normalmente combinados em strings ou
cadeias de caracteres, que possuem um número variável
de caracteres.
♦ Existem 3 opções para representar uma string:
♦ A primeira posição da string é reservada para
armazenar o comprimento da cadeia;
♦ Ex: “casa” = 4 _c_ _a_ _s_ _a_ = 4 99 97 115
97
♦ Qual o tamanho máximo da string se cada
posição 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.
Trabalhando com caracteres e
strings
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.
♦ Os endereços base de x e de y estão em $a0 e $a1,
enquanto que i está em $s0. Precisamos então preservar
o valor de $s0:
strcpy:
addi $sp, $sp, -4 # espaço para 1 item na
pilha
sw $s0, 0($sp) # salva $s0
♦ Inicializamos i com 0:
add $s0, $zero, $zero # i = 0 + 0
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
Exemplo: string_copy
♦ Restauramos $s0, ajustamos $sp e retornamos:
• L2: # fim de string
• lw $s0, 0($sp) # restaura $s0
• addi $sp, $sp, 4 # retira 1 item da
pilha
• jr $ra # retorna para
chamador
♦ Poderíamos ter alocado o registrador $t0 para i, e neste
caso não teríamos que empilhar e desempilhar $s0.
♦ Percebemos que é melhor utilizar primeiro os
registradores $t que $s em funções que não chamam
outra função
Exemplo: string_copy
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.

Mais conteúdo relacionado

Mais procurados

Introduçãso a linguagem c
Introduçãso a linguagem cIntroduçãso a linguagem c
Introduçãso a linguagem csergiocrespo
 
Conjunto de instruções mips - instruções de desvio
Conjunto de instruções mips - instruções de desvioConjunto de instruções mips - instruções de desvio
Conjunto de instruções mips - instruções de desvioElaine Cecília Gatto
 
Introdução a linguagem c karen lowhany
Introdução a linguagem c   karen lowhanyIntrodução a linguagem c   karen lowhany
Introdução a linguagem c karen lowhanyKaren Costa
 
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdf
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdfJogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdf
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdfPauloGomes623362
 
Aula 4 a linguagem assembly
Aula 4   a linguagem assemblyAula 4   a linguagem assembly
Aula 4 a linguagem assemblyLCCIMETRO
 
Introdução a Linguagem C
Introdução a Linguagem CIntrodução a Linguagem C
Introdução a Linguagem Capolllorj
 
Aula 6 emu8086
Aula 6   emu8086Aula 6   emu8086
Aula 6 emu8086LCCIMETRO
 
Algoritmos C/C++ - Aula 01
Algoritmos C/C++ - Aula 01Algoritmos C/C++ - Aula 01
Algoritmos C/C++ - Aula 01Leandro Rezende
 
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”Kratos879
 

Mais procurados (20)

Linguagem C clecioamerico
Linguagem C clecioamericoLinguagem C clecioamerico
Linguagem C clecioamerico
 
Compiladores 7
Compiladores 7Compiladores 7
Compiladores 7
 
Curso de Linguagem C
Curso de Linguagem CCurso de Linguagem C
Curso de Linguagem C
 
Compiladores 1
Compiladores 1Compiladores 1
Compiladores 1
 
Apresentação Linguagem C
Apresentação Linguagem CApresentação Linguagem C
Apresentação Linguagem C
 
Introduçãso a linguagem c
Introduçãso a linguagem cIntroduçãso a linguagem c
Introduçãso a linguagem c
 
Conjunto de instruções mips - instruções de desvio
Conjunto de instruções mips - instruções de desvioConjunto de instruções mips - instruções de desvio
Conjunto de instruções mips - instruções de desvio
 
Introdução a linguagem c karen lowhany
Introdução a linguagem c   karen lowhanyIntrodução a linguagem c   karen lowhany
Introdução a linguagem c karen lowhany
 
Compiladores 5
Compiladores 5Compiladores 5
Compiladores 5
 
Linguagem c
Linguagem cLinguagem c
Linguagem c
 
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdf
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdfJogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdf
Jogos_Eletronicos_Aula_03_Introducao_Linguagem_Lua_2014.pdf
 
Pged 02
Pged 02Pged 02
Pged 02
 
Introducao a LPC
Introducao a LPCIntroducao a LPC
Introducao a LPC
 
Notas deaulas
Notas deaulasNotas deaulas
Notas deaulas
 
Aula 4 a linguagem assembly
Aula 4   a linguagem assemblyAula 4   a linguagem assembly
Aula 4 a linguagem assembly
 
Introdução a Linguagem C
Introdução a Linguagem CIntrodução a Linguagem C
Introdução a Linguagem C
 
Aula 6 emu8086
Aula 6   emu8086Aula 6   emu8086
Aula 6 emu8086
 
TDC2014 SP - C/C++ - Modularização de Código C
TDC2014 SP - C/C++ - Modularização de Código CTDC2014 SP - C/C++ - Modularização de Código C
TDC2014 SP - C/C++ - Modularização de Código C
 
Algoritmos C/C++ - Aula 01
Algoritmos C/C++ - Aula 01Algoritmos C/C++ - Aula 01
Algoritmos C/C++ - Aula 01
 
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”
Apostila C++ Básico - UNIVERSIDADE ESTADUAL PAULISTA “JÚLIO DE MESQUITA FILHO”
 

Semelhante a Oc2 cap03

Introdução à Linguagem de Programação C
Introdução à Linguagem de Programação CIntrodução à Linguagem de Programação C
Introdução à Linguagem de Programação CJose Augusto Cintra
 
Módulo 5 Arquitetura de Computadores
Módulo 5 Arquitetura de ComputadoresMódulo 5 Arquitetura de Computadores
Módulo 5 Arquitetura de ComputadoresLuis Ferreira
 
Programação de Microprocessadores
Programação de MicroprocessadoresProgramação de Microprocessadores
Programação de MicroprocessadoresDiogo Silva
 
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...Alex Camargo
 
Introdução+à+linguagem+c
Introdução+à+linguagem+cIntrodução+à+linguagem+c
Introdução+à+linguagem+cThiago Freitas
 
Aula 2 - Introducao e Algoritmos.ppt
Aula 2 - Introducao e Algoritmos.pptAula 2 - Introducao e Algoritmos.ppt
Aula 2 - Introducao e Algoritmos.pptJadna Almeida
 
Processador CISC RISC SET de instruções
Processador CISC RISC SET de instruçõesProcessador CISC RISC SET de instruções
Processador CISC RISC SET de instruçõesCarlos Pereira
 
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdf
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdfparadigmasdlsksmmskskkekekekekedmmmdmdmmf.pdf
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdfAndreiaCristinaFlore
 
Conceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoConceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoJorge Cardoso
 
Linguagem c wellington telles - aula 02
Linguagem c   wellington telles - aula 02Linguagem c   wellington telles - aula 02
Linguagem c wellington telles - aula 02profwtelles
 
Linguagem de programação
Linguagem de programaçãoLinguagem de programação
Linguagem de programaçãoSandro Lopes
 
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORES
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORESARQUITETURA E ORGANIZAÇÃO DE COMPUTADORES
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORESwillian324163
 
Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Robson Ferreira
 
Lógica de programação pascal
Lógica de programação   pascalLógica de programação   pascal
Lógica de programação pascalJocelma Rios
 
Linguagem C e Dev-C++ Algumas Explicações
Linguagem C e Dev-C++  Algumas ExplicaçõesLinguagem C e Dev-C++  Algumas Explicações
Linguagem C e Dev-C++ Algumas Explicaçõesnataferraz
 

Semelhante a Oc2 cap03 (20)

Introdução à Linguagem de Programação C
Introdução à Linguagem de Programação CIntrodução à Linguagem de Programação C
Introdução à Linguagem de Programação C
 
Módulo 5 Arquitetura de Computadores
Módulo 5 Arquitetura de ComputadoresMódulo 5 Arquitetura de Computadores
Módulo 5 Arquitetura de Computadores
 
Programação de Microprocessadores
Programação de MicroprocessadoresProgramação de Microprocessadores
Programação de Microprocessadores
 
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...
Algoritmos e Programação: Apresentação da ferramenta de programação. Comandos...
 
Apostila c
Apostila cApostila c
Apostila c
 
Introdução+à+linguagem+c
Introdução+à+linguagem+cIntrodução+à+linguagem+c
Introdução+à+linguagem+c
 
Aula 2 - Introducao e Algoritmos.ppt
Aula 2 - Introducao e Algoritmos.pptAula 2 - Introducao e Algoritmos.ppt
Aula 2 - Introducao e Algoritmos.ppt
 
Ud2
Ud2Ud2
Ud2
 
Processador CISC RISC SET de instruções
Processador CISC RISC SET de instruçõesProcessador CISC RISC SET de instruções
Processador CISC RISC SET de instruções
 
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdf
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdfparadigmasdlsksmmskskkekekekekedmmmdmdmmf.pdf
paradigmasdlsksmmskskkekekekekedmmmdmdmmf.pdf
 
Conceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoConceitos Fundamentais de Programacao
Conceitos Fundamentais de Programacao
 
Apostila c++ básico
Apostila c++ básicoApostila c++ básico
Apostila c++ básico
 
Apostila c++ básico (1)
Apostila c++ básico (1)Apostila c++ básico (1)
Apostila c++ básico (1)
 
Linguagem c wellington telles - aula 02
Linguagem c   wellington telles - aula 02Linguagem c   wellington telles - aula 02
Linguagem c wellington telles - aula 02
 
Linguagem de programação
Linguagem de programaçãoLinguagem de programação
Linguagem de programação
 
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORES
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORESARQUITETURA E ORGANIZAÇÃO DE COMPUTADORES
ARQUITETURA E ORGANIZAÇÃO DE COMPUTADORES
 
Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2
 
Lógica de programação pascal
Lógica de programação   pascalLógica de programação   pascal
Lógica de programação pascal
 
Apostila de-arduino
Apostila de-arduinoApostila de-arduino
Apostila de-arduino
 
Linguagem C e Dev-C++ Algumas Explicações
Linguagem C e Dev-C++  Algumas ExplicaçõesLinguagem C e Dev-C++  Algumas Explicações
Linguagem C e Dev-C++ Algumas Explicações
 

Oc2 cap03

  • 1. Conjunto de Instruções Para comandar o hardware do computador, é necessário que falemos sua língua: ♦ As palavras da linguagem de máquina são chamadas de instruções; ♦ O vocabulário forma o conjunto de instruções, que determina a interface hardware/software. ♦ Computadores com arquiteturas diferentes implementam conjuntos de instruções distintos
  • 2. 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
  • 3. 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
  • 4. Implementação de Programas ♦ Um Programa de Computador é, basicamente, uma seqüência de comandos ou instruções representando um algoritmo a ser executado pela máquina. ♦ Normalmente os programadores usam Linguagens de Programação de Alto Nível (LAN), como Pascal e C ♦ Entretanto, estas linguagens correspondem a um nível de abstração elevado. ♦ As linguagens dos processadores como o x86 ou o MIPS correspondem a um nível de abstração baixo, e são denominadas de Linguagens de Baixo Nível (LBN) ♦ Desta maneira é necessário um processo de Tradução
  • 5. 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
  • 6. 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
  • 7. Níveis de Abstração ♦ Diferentes Níveis de Abstração
  • 8. 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).
  • 9. 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
  • 10. Tradutores ♦ Montadores, Compiladores e Interpretadores: montado r execuçã o Código fonte (ling. Montagem) Código Objeto compilado r Código fonte (LAN) Código Objeto execuçã o Interpretador Código fonte (LAN ou Assembl y) execuçã o
  • 11. 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
  • 12. 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 R0 - R31 PC HI LO OP OP OP rs rt rd sa funct rs rt imediato Destino do jump Registradores
  • 13. add a, b, c # a = b + c add a, a, d # a = b + c + d add a, a, e # a = b + c + d + e Operações do Hardware Todo computador deve ser capaz de realizar operações aritméticas. Instruções aritméticas no MIPS têm formato fixo, realizando somente uma operação e tendo três “variáveis” Somente uma instrução por linha Comentários ex: a = b + c + d + e ex..: add a,b,c a = b + c
  • 14. 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 variável de parâmetros é mais complexo que para número fixo. Princípio #1 para projetos: Simplicidade favorece a regularidade
  • 15. Exemplo ♦ Qual o código gerado por um compilador C para o seguinte trecho? add a, b, c #a = b + c sub d, a, e #d = a - e a = b + c; d = a – e;
  • 16. Exemplo 2 ♦ Qual o código gerado por um compilador C para o seguinte trecho? add t0, g, h # temporário t0 = g + h add t1, i, j # temporário t1 = i + j sub f, t0, t1 #f = (g + h) – (I + j) Somente uma operação é feita por instrução: necessidade de variáveis temporárias. f = (g + h) – (i + j);
  • 17. Operandos e Registradores ♦ Ao contrário das linguagens de alto nível, operandos de instruções aritméticas não podem ser quaisquer variá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;
  • 18. 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)
  • 19. Operandos e Registradores ♦ Registradores do MIPS são de 32 bits; ♦ no MIPS, blocos de 32 bits são chamados de palavra; ♦ Número de registradores é limitado: MIPS → 32 registradores, numerados de 0 a 31 ♦ Princípio #2 para projetos: menor é mais rápido ♦ Um número muito grande de registradores aumentaria o período de clock.
  • 20. 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
  • 21. Exemplo Qual o código gerado por um compilador C para o seguinte trecho? 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) As variáveis f, g, h, i, j podem ser mapeadas nos registradores $s0, $s1, $s2, $s3 e $s4, respectivamente. f = (g + h) – (i + j)
  • 22. Operandos e Registradores ♦ Estruturas de dados podem ser bastante complexas, com um número de elementos grande demais para serem armazenados nos registradores ♦ Estruturas de dados, como vetores, são armazenadas na memória ♦ Operações aritméticas em MIPS só podem ser feitas quando os operandos estão em registradores ♦ MIPS deve fornecer instruções para transferir dados entre a memória e os registradores ♦ Para acessar uma palavra na memória, a instrução deve fornecer um endereço de memória
  • 23. 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 DadosEndereços Memória
  • 24. Transferindo dados da memória ♦ A instrução de transferência de dados da memória para o registrador é chamada de load. ♦ Formato: ♦ Endereço de memória acessado é dado pela soma da constante (chamada de offset) com o conteúdo do registrador base Em MIPS, o nome da instrução é: lw (load word) lw registrador destino, constante (registrador base)
  • 25. Exemplo ♦ Suponha que temos um apontador a para um inteiro que está na memória, com o endereço de memória dado no registrador $s3 (ou seja, o apontador a está em $s3). Suponha ainda que o compilador associou as variáveis g e h aos registradores $s1 e $s2. Qual o código para o seguinte trecho em C? ♦ Primeiro temos que pegar o operando que está na memória e transferi-lo para um registrador: g = h + *a; lw $t0, 0($s3) # temporário t0 = *a add $s1, $s2, $t0 # g = h + *a
  • 26. 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]. Vetor na memória Dados Endereços Endereço base de A ... 5 10 0 0 0 0 15 42 ... ... 100 101 102 103 104 105 106 107 ...
  • 27. 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 ♦ Primeiro temos que pegar o operando que está na memória e transferi-lo para um registrador: g = h + A[8] ? lw $t0, 8($s3) # temporário t0 = A[8] add $s1, $s2, $t0 # g = h + A[8]
  • 28. 100 10 101 1 Acesso à memória ♦ Uma vez que bytes (8 bits) são úteis em muitos programas, a maior parte das arquiteturas permite acessar bytes; ♦ Portanto, o endereço de uma palavra é o endereço de um dos 4 bytes dentro da palavra ♦ Assim, endereços de palavras consecutivas diferem em 4 12 8 4 0 Processador DadosEndereços Memória
  • 29. Cada posição do vetor (de inteiros) é uma palavra, e portanto ocupa 4 bytes 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]. Vetor na memória (2) Dados Endereços Endereço base de A ... 5 10 0 0 0 0 15 4 2 ... ... 400 404 408 412 416 420 424 428 ...
  • 30. 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 nona posição do vetor A, A[8], está no offset 8 x 4 = 32 ♦ A décima-terceira posição do vetor A, A[12], está no offset 12 x 4 = 48 A[12] = h + A[8] ? lw $t0, 32($s3) # temporário t0 = A[8] add $t0, $s2, $t0 # temporário t0 = h + A[8] lw $t0, 48($s3) # carrega A[12] em $t0!!!!
  • 31. Transferindo dados para a memória ♦ A instrução de transferência de dados de um registrador para a memória é chamada de store. ♦ Formato: ♦ Endereço de memória acessado é dado pela soma da constante (chamada de offset) com o conteúdo do registrador base No MIPS, o nome da instrução é: sw (store word) sw registrador fonte, constante (registrador base)
  • 32. Endereço absoluto de A[3] ♦ Para obter o endereço absoluto precisamos: 0 1 2 3 4 5 6 7 8 9 ... Registrador base exe. $s2 Variável i ( i = 3) ($s4) deslocamento(offset) offset = 4*i endereço = $s2 + 4*3
  • 33. 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: ♦ 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: g = h + A[i] ? add $t1, $s4, $s4 # $t1 = 2 * i add $t1, $t1, $t1 # $t1 = 4 * i
  • 34. Variável de índice ♦ Para pegarmos A[i], somamos $t1 com o endereço base de A, dado em $s3: ♦ Agora podemos carregar A[i] para um registrador temporário, e realizar a soma, que será armazenada em g: add $t1, $t1, $s3 # $t1 = endereço de A[i] lw $t0, 0($t1) # temporário $t0 = A[i] add $s1, $s2, $t0 # g = h + A[i]
  • 35. Resumo da Tradução g = h + A[i] add $t1, $s4, $s4 # $t1 = 2 * i add $t1, $t1, $t1 # $t1 = 4 * i add $t1, $t1, $s3 # $t1 = endereço de A[i] lw $t0, 0($t1) # temporário $t0 = A[i] add $s1, $s2, $t0 # g = h + A[i]
  • 36. 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] ?
  • 37. Solução add $t0, $s0, $s0 # $t0 = 2*i add $t0, $t0, $t0 # $t0 = 4*i add $t0, $t0, $s2 # $t0 = endereço de A[i] lw $t1, 0($t0) # $t1 = A[i] add $t1, $s1, $t1 # $t1 = g + A[i] lw $t0, 0($s2) # $t0 = A[0] sub $t1, $t1, $t0 # $t1 = g + A[i] – A[0] add $t0, $s0, $s1 # $t0 = i + g add $t0, $t0, $t0 # $t0 = 2 * (i + g) add $t0, $t0, $t0 # $t0 = 4 * (i + g) add $t0, $t0, $s2 # $t0 = endereço de A[i + g] sw $t1, 0($t0) # A[i + g] = g + A[i] – A[0] A[i+g] = g + A[i] – A[0]? i,g,A = $s0, $s1,$s2
  • 38. 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.
  • 39. ♦ MIPS requer que todas as palavras comecem em endereços que são múltiplos de 4 bytes ♦ Chamamos isto de alinhamento: objetos têm que ter endereços que sejam múltiplos de seus tamanhos. 0 1 2 3 Alinhado Não alinhado Alinhamento de Dados na Memória
  • 40. ♦ Processadores podem numerar bytes dentro de uma palavra, de tal forma que o byte com o menor número é o mais a esquerda ou o mais a direita. Isto é chamado de byte order. ♦ Ex: .byte 0, 1, 2, 3 ♦ Big endian: IBM 360/370, Motorola 68k, MIPS, Sparc, HP PA ♦ Little Endian: Intel 80x86, MIPS, DEC Vax, DEC Alpha 3 2 1 0little endian 0 1 2 3big endian Ordenamento dos Bytes
  • 41. 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
  • 42. Representando Instruções no Computador ... ♦ Linguagem de máquina para a instrução ♦ 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. 0 17 18 8 0 32 add $t0, $s1, $s2
  • 43. ♦ Também podemos representar tais números em binário: ♦ Para distinguir da linguagem Assembly, chamamos esta representação numérica de linguagem de máquina, e a seqüência de tais instruções de código de máquina ♦ Chamamos esta estrutura de campos de formato da instrução. Todas as instruções em MIPS possuem 32 bits (“simplicidade favorece regularidade”). 000000 10001 10010 01000 00000 100000 Representando Instruções no Computador ... 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
  • 44. Formato Instrução MIPS ♦ Daremos nomes aos campos para simplificar a discussão: ♦ 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 op rs rt rd shamt funct6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
  • 45. 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.
  • 46. Formato Instrução MIPS... ♦ Princípio #3 para projetos: bons projetos demandam bons compromissos. ♦ Compromisso escolhido pelos projetistas do MIPS: manter todas as instruções com o mesmo tamanho → diferentes tipos de instrução podem ter diferentes formatos de instrução. ♦ O formato mostrado anteriormente é chamado de tipo-R (de registrador) ou formato-R ♦ Um segundo formato é usado pelas instruções de transferência de dados: tipo-I ou formato-I
  • 47. Formato Instrução MIPS... ♦ Formato tipo-I: ♦ 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. op rs rt endereço 6 bits 5 bits 5 bits 16 bits
  • 48. Formato Instrução MIPS... ♦ Ex: lw $t0, 32($s3) #temporário $t0 = A[8] ♦ 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). op rs rt endereço
  • 49. Formato Instrução MIPS... ♦ Primeiro campo determina tipo do formato. ♦ Pergunta: por que não utilizar somente um campo de 12 bits para os campos op e funct nas instruções de tipo-R??
  • 50. Codificação das Instruções Vistas Instruçã o Formato op rs rt rd shamt func t 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
  • 51. 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 é: ♦ Qual o código de máquina destas 3 instruções? lw $t0,1200($t1) # $t0 = A[300] add $t0, $s2, $t0 # $t0 = h + A[300] sw$t0, 1200($t1) # A[300] = h + A[300]
  • 52. Exemplo de compilação manual ... lw $t0,1200($t1) add $t0, $s2, $t0 sw $t0, 1200($t1) op rs r t r d Endereço / shamt func t 35 9 8 1200 0 1 8 8 8 0 32 43 9 8 1200 op rs rt rd Endereço / shamt funct 100011 01001 01000 0000 0100 1011 0000 000000 10010 01000 01000 00000 100000 101011 01001 01000 0000 0100 1011 0000
  • 53. 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!!!
  • 54. 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)
  • 55. Instruções de decisão no MIPS ♦ em C, isto seria equivalente a: beq registr1, registr2, L1 Semântica: “Desvie se (valores nos registradores são) iguais” if (registr1== registr2) goto L1
  • 56. Instruções de decisão no MIPS ... ♦ em C, isto seria equivalente a: ♦ Estas instruções são chamadas de desvios condicionais. bne registr1, registr2, L1 Semântica: “desvie se (valores nos registradores) não são iguais” if (registr1!=registr2) goto L1
  • 57. 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? Como instruções são armazenadas na memória, elas têm endereços também!! 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)
  • 58. Instruções para tomada de decisões ♦ O montador (assembler) faz com que o compilador ou o programador em linguagem assembly não tenha que ficar calculando endereços para os desvios (branches) ao permitir o uso de rótulos. ♦ Compiladores criam desvios e rótulos sem que o programador de linguagens de alto nível os tenha que especificar. Esta é uma das razões de por que é mais rápido programar em linguagens de alto nível.
  • 59. 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: • 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. j rotulo beq $0,$0,rotulo
  • 60. 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? ♦ Queremos implementar o seguinte fluxo: Fim i == j? f=g+h f=g-h (falso) i != j (verdadeiro) i == j if (i == j) f = g + h; else f = g – h;
  • 61. Exemplo: código para if Primeiro devemos saber se i é igual a j: Se i == j, somamos g com h e armazenamos em f Precisamos agora ir para o fim do if. Para isto utilizamos um desvio incondicional ( j, de jump ): Tratamos agora o caso em que i != j bne $s3, $s4, else # vá para else se i != j 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:
  • 62. 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.
  • 63. 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)
  • 64. Compilando um laço do tipo while ♦ Programadores normalmente não utilizam comandos goto (o uso deles não é considerado boa prática de programação!!!). Assim, o compilador tem que traduzir os loops tradicionais em linguagem MIPS. ♦ Exemplo em C: ♦ Se i, j, e k correspondem aos registradores $s3, $s4 e $s5, e o endereço base do vetor save está no registrador $s6, qual o código assembly MIPS correspondente? while (save[j] == k) i = i + j;
  • 65. ♦ Temos inicialmente que carregar save[i] para um registrador temporário: ♦ Agora fazemos o teste do loop, saindo se save[i] != k ♦ Devemos agora voltar para o while no inicio do loop 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] bne $t0, $s5, Fim # vá para Fim se save[i] != k add $s3, $s3, $s4 # i = i + j j Loop # vá para Loop Fim: Compilando um laço do tipo while
  • 66. 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: ♦ → Se $s3 < $s4, $t0 recebe 1, caso contrário, recebe 0 slt (set on less than) slt $t0, $s3, $s4
  • 67. Compilando um laço do tipo while ♦ Compiladores MIPS utilizam as instruções slt, beq, bne, e o valor fixo zero para criar todas as relações de comparação: igual, diferente, menor que, menor ou igual a, maior que e maior ou igual a. ♦ O registrador $zero (registrador 0) tem seu valor fixado em zero.
  • 68. 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: ♦ Registrador $t0 é 1 se a < b. Portanto, testamos se $t0 não é 0: slt $t0, $s0, $s1 # $t0 = (a < b)? bne $t0, zero, Menor # vá para Menor se $t0!=0 # ou seja, se (a < b)
  • 69. 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:
  • 70. Por que não utilizar uma instrução só para “desvie se menor que”? ♦ MIPS não tem instruções como “desvie se menor que” porque elas são complicadas demais: ou elas aumentariam o período de clock ou seriam necessários mais ciclos de clock por instrução (além de fazer com que fosse necessário mais hardware). ♦ As duas instruções slt e bne são mais rápidas e mais úteis, pois também servem para outros propósitos.
  • 71. Um outro tipo de desvio incondicional ♦ Até agora vimos uma instrução de desvio incondicional, através da instrução ♦ 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: j Rotulo # desvio para rótulo jr registrador #desvio para endereço #contido no registrador
  • 72. 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; }
  • 73. Comando switch ♦ Uma forma de se fazer isto é tratar o comando switch como sendo uma seqüência de comandos if: if ( (k >= 0) && (k <= 3)) if (k == 0) f = i + j; else if (k == 1) f = g + h; else if (k == 2) f = g - h; else f = i + j;
  • 74. 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.
  • 75. Comando switch ♦ Melhorando o comando switch: ♦ ♦ 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. 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; }
  • 76. Comando switch ♦ Utilizaremos a variável k como índice na tabela de endereços, e desviar de acordo com o valor carregado; ♦ Primeiro, temos que verificar se 0 <= k <= 3: ♦ Vamos utilizar k para indexação; por isto temos que multiplicar k por 4. slt $t3, $s5, $zero # teste se k<0 bne $t0, $zero, Fim # se k<0, vá para Fim slt $t3, $s5, $t2 # teste se k<4 beq $t3, $zero, Fim # se k>=4 vá para Fim
  • 77. Comando switch ♦ 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: ♦ Agora saltamos para o endereço presente em $t0: add $t1, $s5, $s5 # t1 = 2 * k add $t1, $t1, $t1 # t1 = 4 * k add $t1, $t1, $t4 # t1 = endereço de TabEnd[k] lw $t0, 0($t1) # t0 = TabEnd[k] jr $t0
  • 78. 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:
  • 79. Resumo: instruções vistas até agora Categoria Instrução Exemplo Semântica Adição add $s1, $s2, $s3 $s1 = $s2 + $s3Aritmética Subtração sub $s1, $s2, $s3 $s1 = $s2 - $s3 Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100]Transferência de dados Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1 Branch on equal beq $s1, $s2, L If ($s1 == $s2) goto L Branch on not equal bne $s1, $s2, L If ($s1 != $s2) goto L Desvio Condicional Set on less than slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0; Jump j 2500 goto 10000Desvio Incondicional Jump register jr $t1 goto $t1
  • 80. 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.
  • 81. 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.
  • 82. Instruções de suporte a procedimentos ♦ Qual o lugar mais rápido que pode armazenar dados em um computador? ♦ Registrador. Queremos utilizar registradores sempre que possível ♦ Software para MIPS aloca os seguintes registradores para chamada de procedimentos: ♦ $a0 - $a3: quatro registradores que são utilizados para passar parâmetros para os procedimentos; ♦ $v0 - $v1: dois registradores que são utilizados para retornar valores calculados pelos procedimentos (resultados) ♦ $ra: registrador utilizado para retornar para o ponto de origem (ra = return address).
  • 83. Valores de retorno do procedimento Desvio para procedimento Instruções de suporte a procedimentos Execução do procedimento Preparo argumentos para o procedimento $a0-$a3 $ra; desvi o $v0-$v1 Continuação do programa Retorno para a continuação do programa jr $ra
  • 84. C M I P S Exemplo de procedimento ... soma(a,b);... /* a:$s0; b:$s1 */ } int soma(int x, int y) { /* x:$a0; y:$a1 */ return x+y; } 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
  • 85. jal (jump and link) ♦ O assembly MIPS tem uma instrução de desvio só para procedimentos; ela causa um desvio para um endereço, e ao mesmo tempo guarda o endereço da próxima instrução em $ra: jal (jump and link). jal Rotulo #desvio para o procedimento que tem #endereço marcado por Rótulo
  • 86. 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: ♦ Por que existe a instrução jal?? Procedimentos são muito comuns: “faça o caso comum ser rápido” $ra = PC + 4 j Rótulo
  • 87. C M I P S Exemplo de procedimento (revisado) ... 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
  • 88. Exercício ♦ Qual o código em assembly para MIPS equivalente ao seguinte trecho em C, estando i em $s0 e j em $s1? • i = sub(i,j); /* sub(a, b) {return a-b;} */ ... add $a0, $s0, $zero # a0 = i add $a1, $s1, $zero # a1 = j jal sub # chama a função sub add $s0, $v0, $zero # i = sub(i,j) ... sub: sub $v0, $a0, $a1 # $v0 = a – b jr $ra # retorne para origem
  • 89. 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.
  • 90. 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)
  • 91. Usando mais registradores ♦ Por razões históricas, a pilha “cresce” do maior endereço para o menor endereço: ♦ Para colocar um valor na pilha (push), devemos decrementar $sp em uma palavra e mover o valor desejado para a posição de memória apontada por $sp; ♦ Para retirar um valor da pilha (pop), devemos ler este valor da posição de memória apontado por $sp, e então incrementar $sp em uma palavra.
  • 92. 0 Endereço Código Programa Estático Variáveis declaradas uma vez para todo programa Heap Espaço explicitamente criado malloc: apontadores em C Pilha Espaço para procedimentos armazenarem informações$sp stack pointer Alocação de memória em C
  • 93. Exemplo: exemplo_proc ♦ Suponha que tenhamos o seguinte código: Vamos gerar o código correspondente em assembly MIPS. int exemplo_proc (int g, int j, int i, int h) { int f; f = (g+h) – (i+j); return f; }
  • 94. Exemplo : exemplo_proc ♦ Os argumentos g, h ,i e j correspondem aos registradores $a0, $a1, $a2 e $a3, e f corresponde a $s0. Primeiro colocamos o rótulo do procedimento: ♦ exemplo_proc: ♦ Devemos então armazenar os registradores que serão utilizados pelo procedimento. Como estaremos utilizando os registradores $t0, $t1 e $s0, vamos colocá-los na pilha: subi $sp, $sp, 12 # cria espaço para mais 3 itens na pilha sw $t1, 8(sp) # empilha $t1 sw $t2, 4(sp) # empilha $t2 sw $s0, 0(sp) # empilha $s0
  • 95. Exemplo : exemplo_proc Como ficou a pilha? $sp Valores empilhados antes da função Pilha antes da função $t1 $t0 $s0$sp Valores empilhados antes da função Pilha durante execução da função
  • 96. Exemplo : exemplo_proc ♦ As próximas instruções correspondem ao corpo do procedimento: ♦ O resultado deve ser armazenado no registrador $v0: add $v0, $s0, $zero # retorna f em $v0 add $t0, $a0, $a1 # $t0 = g + h add $t1, $a2, $a3 # $t1 = i + j sub $s0, $t0, $t1 # f = $t0 = (g+h) – (i+j)
  • 97. Exemplo : exemplo_proc ♦ Antes de sair do procedimento, devemos restaurar os valores de $t0, $t1 e $s0, retirando-os da pilha: ♦ Voltamos então para a instrução seguinte ao ponto em que a função exemplo_proc foi chamada: lw $s0, 0($sp) # desempilha $s0 lw $t0, 4($sp) # desempilha $t0 lw $t1, 8 ($sp) # desempilha $t1 addi $sp, $sp, 12 # remove 3 itens da pilha jr $ra # retorna para a subrotina que chamou # este procedimento
  • 98. 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.
  • 99. 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)??
  • 100. Procedimentos aninhados ♦ Suponha que o programa principal chama a função A com o argumento 3, ao colocar 3 em $a0 e utilizar a instrução jal A. ♦ Suponha ainda que a função A chama a função B com argumento 7, ao colocar 7 em $a0 e utilizar a instrução jal B. ♦ Uma vez que A ainda não terminou de executar temos um conflito no uso do registrador $a0. ♦ Um problema também aparece para o registrador $ra, que agora contém o valor para retorno de B. ♦ Se não tomarmos cuidado, poderemos não ser capazes nem de voltarmos para o programa principal!!! ♦ Como resolver?
  • 101. 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;
  • 102. 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.
  • 103. Direitos da Chamadora e da Chamada ♦ Direitos da Chamada: • Utilizar livremente os registradores v, a, e t; • Assumir que os argumentos são passados corretamente. ♦ Registradores que devem ser preservados pela Chamadora: • Endereço de retorno $ra • Argumentos $a0, $a1, $a2, $a3 • Valores de retorno $v0, $v1 • Registradores temporários $t0 - $t9
  • 104. ♦ 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 Direitos da Chamadora e da Chamada
  • 105. Exemplo: soma_recursiva ♦ Suponha que tenhamos o seguinte código, que calcula a soma n + (n-1) + … + 2 + 1 de forma recursiva: ♦ Vamos gerar o código correspondente em assembly MIPS. int soma_recursiva (int n) { if (n < 1) return 0; else return n + soma_recursiva(n- 1) }
  • 106. Exemplo: soma_recursiva ♦ O parâmetro n corresponde ao registrador $a0. ♦ Devemos inicialmente colocar um rótulo para a função, e salvar o endereço de retorno, armazenado em $ra, o parâmetro $a0: ♦ Na primeira vez que soma_recursiva é chamada, o valor de $ra que é armazenado corresponde a um endereço que está na sub-rotina que chama esta função Soma_recursiva: subi $sp, $sp, 8 # prepara a pilha para receber 2 # valores sw $ra, 4($sp) # empilha $ra sw $a0, 0($sp) # empilha $a0
  • 107. Exemplo: soma_recursiva ♦ Vamos agora compilar o corpo da função. Inicialmente, testamos se n < 1: ♦ Se n >=1, a função deve retornar o valor 0. Não podemos nos esquecer de restaurar a pilha. ♦ Por que não carregamos os valores de $a0 e $ra antes de ajustar $sp?? slti $t0, $a0, 1 # testa se n < 1 beq $t0, $zero, L1 # se n>=1, vá para L1 add $v0, $zero, $zero # valor de retorno é 0 add $sp, $sp, 8 # remove 2 itens da pilha jr $ra # retorne para depois de jal
  • 108. Exemplo: soma_recursiva ♦ Se n >=1, decrementamos n e chamamos novamente a função soma_recursiva com o novo valor de n. ♦ 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: L1: subi $a0, $a0, 1 # argumento passa a ser (n-1) jal soma_recursiva # calcula a soma para (n-1) lw $a0, 0($sp) # restaura o valor de n lw $ra, 4($sp) # restaura o endereço de retorno addi $sp, $sp, 8 # retira 2 itens da pilha.
  • 109. Exemplo: soma_recursiva ♦ Agora o registrador $v0 recebe a soma do argumento antigo $a0 com o valor atual em $v0 (soma_recursiva para n-1): ♦ Por último, voltamos para a instrução seguinte à que chamou o procedimento: add $v0, $a0, $v0 # retorne n + soma_recursiva(n-1) jr $ra # retorne para o “chamador”
  • 110. 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.
  • 111. 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)
  • 112. Exemplo : $fp e $sp $sp Registro de ativação anterior (outra função) Pilha antes da função $fp Registradores $s salvos (se houver) Registradores $a salvos (se houver) $ra salvo Estruturas e vetores locais (se houver)$sp Registro de ativação anterior (outra função) Pilha durante execução da função $fp
  • 113. 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 Registradores MIPS: resumo da convenção de software 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.10
  • 114. Categoria Instrução Exemplo Semântica Adição add $s1, $s2, $s3 $s1 = $s2 + $s3Aritmética Subtração sub $s1, $s2, $s3 $s1 = $s2 - $s3 Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100]Transferência de dados Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1 Branch on equal beq $s1, $s2, L If ($s1 == $s2) goto L Branch on not equal bne $s1, $s2, L If ($s1 != $s2) goto L Desvio Condicional Set on less than slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0; Jump j 2500 goto 10000 Jump register jr $t1 goto $t1 Desvio Incondicional Jump & link jal 2500 $ra = PC + 4; goto 10000 Resumo: instruções vistas até agora
  • 115. Trabalhando com caracteres e strings ♦ A maior parte dos computadores hoje utiliza bytes para representar caracteres, quase sempre utilizando a codificação ASCII (American Standard Code for Information Interchange). ♦ A seção 3.7 traz a tabela ASCII, que deve ser utilizada como fonte de consulta. ♦ Instruções de transferência de palavras (lw, sw) são suficientes para transferir bytes também. Neste caso, bytes individuais poderiam ser lidos ou escritos, através do uso de máscaras, com as instruções e e ou lógicos (serão vistas no capítulo 4).
  • 116. t No entanto, como grande parte dos programas utiliza texto, MIPS fornece instruções específicas para mover bytes. t Leitura de bytes: t load byte (lb): lê um byte da memória, colocando-o nos 8 bits mais à direita de um registrador t Ex: lb $t0, 0($sp) # lê byte que está no topo da pilha Trabalhando com caracteres e strings
  • 117. ♦ 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 Trabalhando com caracteres e strings
  • 118. ♦ Caracteres são normalmente combinados em strings ou cadeias de caracteres, que possuem um número variável de caracteres. ♦ Existem 3 opções para representar uma string: ♦ A primeira posição da string é reservada para armazenar o comprimento da cadeia; ♦ Ex: “casa” = 4 _c_ _a_ _s_ _a_ = 4 99 97 115 97 ♦ Qual o tamanho máximo da string se cada posição ocupa um byte? Trabalhando com caracteres e strings
  • 119. ♦ 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. Trabalhando com caracteres e strings
  • 120. 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.
  • 121. ♦ Os endereços base de x e de y estão em $a0 e $a1, enquanto que i está em $s0. Precisamos então preservar o valor de $s0: strcpy: addi $sp, $sp, -4 # espaço para 1 item na pilha sw $s0, 0($sp) # salva $s0 ♦ Inicializamos i com 0: add $s0, $zero, $zero # i = 0 + 0 Exemplo: string_copy
  • 122. ♦ 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
  • 123. ♦ 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 Exemplo: string_copy
  • 124. ♦ Restauramos $s0, ajustamos $sp e retornamos: • L2: # fim de string • lw $s0, 0($sp) # restaura $s0 • addi $sp, $sp, 4 # retira 1 item da pilha • jr $ra # retorna para chamador ♦ Poderíamos ter alocado o registrador $t0 para i, e neste caso não teríamos que empilhar e desempilhar $s0. ♦ Percebemos que é melhor utilizar primeiro os registradores $t que $s em funções que não chamam outra função Exemplo: string_copy
  • 125. 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.