Arquitetura e Organização
de Computadores
• Material adaptado de:
Patterson e Henessy,
Conjunto de Instruções - ISA
• É o repertório de instruções de um computador;
• Computadores diferentes possuem conjuntos de instruções diferentes:
• Mas em muitos aspectos eles são comuns;
• Computadores antigos tinham conjuntos de instruções bem simples:
• Implementação simplificada;
• Muitos computadores modernos também possuem conjunto de instruções simplificados.
MIPS Instruction Set
• Usado como exemplo durante este curso;
• Stanford MIPS comercializado pelo MIPS Technologies
(www.mips.com);
• Grande parte do mercado de sistemas embarcados:
• Aplicações em Consumer Eletronics, Network/Storage, equipamentos, câmeras, impressoras, ...
• Típico de muitos ISAs modernos:
Operações Aritméticas
• Todo computador deve ser capaz de executar operações aritméticas:
• Notação da MIPS Assembly Language:
instrui o computado a adicionar duas variáveis b e c e colocar o resultado da soma em a:
add a, b, c
Operações Aritméticas
• Essa notação é fixa, ou seja, sem variações.
• Se você quiser fazer ...
então é necessário...
a = b + c + d + e
add a, b, c # The sum of b and c is placed in a. add a, a, d # The sum of b, c, and d is now in a. add a, a, e # The sum of b, c, d, and e is now in a.
Princípio de Projeto 1
• “Simplicidade favorece a regularidade”;
• Todas as operações aritméticas possuim o mesmo formato;
• Regularidade faz implementação mais simples;
Exemplo: Operações
aritméticas
• Código C:
f = (g + h) - (i + j);
• Código compilado do MIPS:
add t0, g, h # temp t0 = g + h add t1, i, j # temp t1 = i + j sub f, t0, t1 # f = t0 - t1
Operandos em registradores
• Instruções aritméticas usam operandos que vem de registradores;
• MIPS possui 32 x32-bit registradores (banco de registradores):
• Usado para dados que são frequentemente utilizados;
• Numerados de 0 a 31;
• 32-bit de dados é chamado de “word”;
• Nomes dos registradores:
• $t0, $t1, ..., $t9 para dados temporários (variáveis do compilador);
• $s0, $s1, ..., $s7 para dados não-temporários (variáveis do programador).
Princípio de Projeto 2
• “Menor é mais rápido”;
• Um número grande de registradores pode aumentar o tempo do ciclo de clock, tornando o sistema lento.
Exemplo: Operandos em
registradores
• Código C:
f = (g + h) - (i + j);
• f, …, j in $s0, …, $s4
• Código compilado do MIPS: add $t0, $s1, $s2
add $t1, $s3, $s4 sub $s0, $t0, $t1
Como computadores podem
representar e acessar grandes
estruturas de dados?
• Linguagens de programação possuem variáveis que
contem dados simples, mas eles também tem estruturas de dados mais complexas (arrays e structures):
• Podem conter mais dados do que o número de registradores em um computador.
Operandos na Memória
• Memória principal é usada para dados compostos:
• Arrays, structures, dynamic data;
• Para realizar operações aritméticas:
• Carregar valores da memória em registadores;
• Armazenar resultados do registrador na memória.
• Memória é endereçada a bytes:
Operandos na memória
• “Words” são alinhadas na memória:
• Endereços tem que ser múltiplo de 4 (4 Bytes => 32 bits);
• MIPS é Big Endian:
• Byte mais significativo está no menor endereço da palavra;
• OBS: Little Endian; Byte menos significativo está no menor endereço da palavra.
OBS: O que é a memória?
• Memória é um grande unidimensional array com o endereço atuando como índice deste array;
Carregar dados da memória
• A instrução que copia dados da memória para os registradores é tradicionalmente chamada de load;
• A que faz a operação inversa é chamada de store;
• A instrução de load é composta pelo registrador destino, uma constante e registrador de base:
• A soma da constante com o registrador base é utilizado para acessar a memória;
Exemplo 1: Operandos na
memória
• Código C:
g = h + A[8];
• g in $s1, h in $s2, base address of A in $s3
• Código compilado do MIPS:
• Índice 8 requer um deslocamento (offset) de 32
• 4 bytes por word
lw $t0, 32($s3) # load word add $s1, $s2, $t0
Exemplo 2: Operandos na
memória
• Código C:
A[12] = h + A[8];
• h está em $s2, endereço base de A está em $s3
• Código compilado para o MIPS:
• Índice 8 requer um deslocamento (offset) de 32
• Índice 12 requer um deslocamento (offset) de 48
lw $t0, 32($s3) # load word add $t0, $s2, $t0
Operandos Imediatos
• São constantes especificadas em uma instrução: addi $s3, $s3, 4
• Não possui instrução de subtração com imediato:
• Usar constantes negativas: addi $s2, $s1, -1
Princípio de Projeto 3
• “Faça o caso comum mais rápido”
• Pequenas constantes são comuns ao programas;
A constante Zero
• O registrador 0 ($zero) é a constante 0:
• Não pode ser sobrescrito;
• Útil para operações comuns:
• Ex: mover dados entre registradores:
NÚMEROS INTEIROS COM
OU SEM SINAL
Números Inteiros Binários
sem Sinal
• Dado um número de n-bits:
• Alcance: 0 até 2n-1 • Exemplo: • 0000 0000 0000 0000 0000 0000 0000 10112 = 0 + … + 1×23 + 0×22 +1×21 +1×20 = 0 + … + 8 + 0 + 2 + 1 = 1110 • Para 32 bits: • 0 até +4.294.967.295 0 0 1 1 2 n 2 n 1 n 1 n
2
x
2
x
2
x
2
x
x
Inteiros em Complemento a
2 com Sinal
• Dado um número de n-bits:
• Alcance: -2n-1 até 2n-1 – 1 • Exemplo: • 1111 1111 1111 1111 1111 1111 1111 11002 = –1×231 + 1×230 + … + 1×22 +0×21 +0×20 = –2,147,483,648 + 2,147,483,644 = –410 • Para 32 bits: • -2.147.483.648 até +2.147.483.647 0 0 1 1 2 n 2 n 1 n 1 n
2
x
2
x
2
x
2
x
x
Inteiros em Complemento a
2 com Sinal
• Bit 31 é o bit de sinal:
• 1 para números negativo;
• 0 para não números não-negativos;
• -(-2n-1) não pode ser representado;
• Números não-negativos tem a mesma representação em inteiros sem sinal e em complemento a dois;
• Alguns números específicos:
• 0: 0000 0000 … 0000
• –1: 1111 1111 … 1111
• Mais-negativo: 1000 0000 … 0000
Negação com Sinal
• Complemento dos bits somados a 1:
• Complemento significa: 1 → 0, 0 → 1 • Exemplo: negar +2 • +2 = 0000 0000 … 00102 • –2 = 1111 1111 … 11012 + 1 = 1111 1111 … 11102
x
1
x
1
1
1
1
1
...1
1
1
x
x
2
Extensão de Sinal
• Representando um número utilizando mais bits:
• Preservar o valor numérico;
• No ISA do MIPS:
• addi: estende o valor do imediato;
• lb, lh: estende o valor do byte/halfword carregados;
• beq, bne: estente o valor do deslocamento;
• Replicar o valor do sinal para esquerda:
• Valores sem sinal: sempre estender com 0s;
• Exemplo: 8-bit para 16-bit
• +2: 0000 0010 => 0000 0000 0000 0010
REPRESENTANDO
INSTRUÇÕES
Representação de uma
instrução
• Instruções são codificadas em binário (números):
• Chamado de código de máquina:
• Instruções do MIPS:
• Codificadas como palavras de 32-bits;
• Um pequeno número de formatos codificam o código da operação (opcode), números dos registadores, ...
• Regularidade!
• Números dos registradores:
• $t0 - $t7 são os registradores números 8 – 15;
• $t8 - $t9 são os registradores números 24 – 25;
Instruções do formato R
• Campos da instrução:
• op: código da operação (opcode);
• rs: número do primeiro registrador fonte;
• rt: número do segundo registrador fonte;
• rd: número do registrador de destino;
• shamt: quantidade de shifs (00000 por enquanto);
• funct: código da função (estende o opcode).
op rs rt rd shamt funct
Exemplo do formato R
• add $t0, $s1, $s2
op rs rt rd shamt funct
6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
special $s1 $s2 $t0 0 add
0 17 18 8 0 32
000000 10001 10010 01000 00000 100000
Hexadecimal
• Base 16:
• Representação compacta de cadeias de bits;
• 4 bits por cada dígito hexadecimal;
• Example: eca8 6420
Instruções do formato I
• Operações aritméticas com imediatos e instruções de load/store:
• rt: número do registrador destino ou fonte;
• Constante: -215 até +215-1;
• Endereço: deslocamento (offset) adicionado ao registrador base rs;
op rs rt constant or address
Princípio de Projeto 4
• “Bons projetos demandam bons compromissos”;
• Diferentes formatos complicam a decodificação das
instruções, mas permite instruções do mesmo tamanho;
• Mantenha os formatos das instruções o mais similar possível:
• R-type (para registradores) ou R-format.
Exemplo: Traduzindo Assembly para
código de máquina
• Se $t1 tem o endereço base do array A e $s2 corresponde ao, então a sentença:
é compilada para:
• Qual é o código de máquina para essas três instruções?
A[300] = h + A[300];
lw $t0,1200($t1) # Temporary reg $t0 gets A[300] add $t0,$s2,$t0 # Temporary reg $t0 gets h + A[300] sw $t0,1200($t1) # Stores h + A[300] back into A[300]
Computadores com
programas armazenados
• Instruções são representados em binários, como os dados;
• Instruções e dados são
armazenados na memória;
• Programas podem operar sobre programas; • Compatibilidade binária permite programas funcionarem em diferentes computadores • ISAs padronizados.
Operações Lógicas
• Instruções para manipulação da cadeia de bits:
• Útil para extração ou inserção de grupos de bits em uma palavra.
Operações de shift
• Desloca a cadeia de bits para esquerda ou direita;
• shamt: número de bits para deslocar;
• Shift Left Logical (sll):
• Desloca para esquerda e preenche com zeros no final;
• sll de i-bits multiplica a palavra por 2i;
• Shift Right Logical (srl):
• Desloca para direita e preenche com zeros no início;
• srl de i-bits divide a palavra por 2i (só para números sem
sinal).
op rs rt rd shamt funct
Operações de AND
• Útil para mascarar bits em uma palavra:
• Selecionar alguns bits, limpando os outros com 0; and $t0, $t1, $t2 0000 0000 0000 0000 0000 1101 1100 0000 0000 0000 0000 0000 0011 1100 0000 0000 $t2 $t1 0000 0000 0000 0000 0000 1100 0000 0000 $t0
Operações de OR
• Útil para incluir bits em uma palavra:
• Seta alguns bits para 1 sem mudar os outros; or $t0,$t1, $t2 0000 0000 0000 0000 0000 1101 1100 0000 0000 0000 0000 0000 0011 1100 0000 0000 $t2 $t1 0000 0000 0000 0000 0011 1101 1100 0000 $t0
Operações de NOT
• Útil para inverter bits em uma palavra:
• Muda 0 para 1, e 1 para 0;
• MIPS tem uma operação de NOR com 3 operandos:
• a NOR b == NOT (a OR b)
nor $t0, $t1, $zero Register 0: always read as zero
0000 0000 0000 0000 0011 1100 0000 0000 $t1
1111 1111 1111 1111 1100 0011 1111 1111 $t0
INSTRUÇÕES PARA TOMAR
DECISÕES
O diferencial do computador
• O que diferencia um computador de uma calculadora simples é a habilidade de tomar decisões;
• A tomada de decisão é representada em linguagens de programação através das sentenças de if, algumas vezes combinadas com sentenças de go to e rótulos (labels).
Operações condicionais
• Desvia (branch) para um rótulo se a condição da instrução for verdadeiro:
• Caso contrário, continua sequencialmente;
• beq rs, rt, L1
• Se (rs == rt) então desvia para a instrução rotulada de L1;
• bne rs, rt, L1
• Se (rs != rt) então desvia para a instrução rotulada de L1;
• j L1
Exemplo: Compilando
sentenças if
• Código C: if (i==j) f = g+h; else f = g-h; • f, g, … em $s0, $s1, …• Código compilado do MIPS:
bne $s3, $s4, Else add $s0, $s1, $s2 j Exit
Else: sub $s0, $s1, $s2 Exit: …
Exemplo: Compilando
sentenças de Loop
• Código C:
while (save[i] == k) i += 1;
• i em $s3, k em $s5, endereço base do save está $s6
• Código compilado do MIPS:
Loop: sll $t1, $s3, 2 add $t1, $t1, $s6 lw $t0, 0($t1) bne $t0, $s5, Exit addi $s3, $s3, 1 j Loop Exit: …
Blocos básicos
• Um bloco básico é uma sequência de instruções com:
• Nenhuma instrução de desvio (exceto no final)
• Nenhum alvo de uma instrução de desvio (exceto no começo)
• Um compilador identifica blocos básicos para otimizações;
• Um processador avançado pode acelerar a execução de um bloco básico.
Mais operações condicionais
• Seta o resultado para 1 se a condição é verdadeira:
• Caso contrário, seta para 0;
• slt rd, rs, rt
• Se (rs < rt) então rd = 1, caso contrário rd = 0;
• slti rt, rs, constante
• Se (rs < constante) então rt = 1, caso contrário rt = 0;
• Usado em combinação com beq, bne:
• slt $t0, $s1, $s2 # if ($s1 < $s2) bne $t0, $zero, L # branch to L
Projeto das instruções de
desvio
• Por que não blt, bge, etc?
• Hardware para <, ≥, … é mais lento que =, ≠
• Combinando isso com o branch em si envolve mais trabalho por instrução, levando um clock mais lento;
• Todas as intruções seriam penalizadas!
• beq e bne são os casos mais comuns;
Sinal vs. Sem sinal
• Comparação com sinal: slt, slti
• Comparação sem sinal: sltu, sltui
• Exemplo: • $s0 = 1111 1111 1111 1111 1111 1111 1111 1111 • $s1 = 0000 0000 0000 0000 0000 0000 0000 0001 • slt $t0, $s0, $s1 # signed • –1 < +1 $t0 = 1 • sltu $t0, $s0, $s1 # unsigned • +4,294,967,295 > +1 $t0 = 0
CHAMADA DE
Chamada de procedimento
• Passos de uma chamada de procedimento:
1. Colocar parâmetros nos registradores;
2. Transferir o controle para o procedimento;
3. Adquirir espaço de armazenamento para o procedimento;
4. Executar as operações do procedimento;
5. Colocar o resultado em um registrador para quem chamou o procedimento;
Convenção: Uso de
registradores
• $a0 – $a3: parâmetros (reg’s 4 – 7);
• $v0, $v1: retorno (reg’s 2 e 3);
• $t0 – $t9: temporários:
• Podem ser sobrescritos pelo procedimento;
• $s0 – $s7: “salvos” ou globais:
• Devem ser salvos/recuperados pelo procedimento;
• $gp: Ponteiro global para dados estáticos (reg 28);
• $sp: Ponteiro da pilha (stack pointer) (reg 29);
• $fp: Ponteiro da janela (frame pointer) (reg 30);
Instruções para chamar
procedimentos
• Chamada de procedimento: jump and link (pular e conectar):
jal <rótulo>
• Salva o endereço da instrução seguinte ao jal no $ra (link);
• Pula para o endereço alvo (rótulo);
• Retorno de procedimento: jump register (pular para o registrador):
jr $ra
Exemplo: Chamada de
procedimento “folha”
• C code:
int leaf_exemple (int g, h, i, j) { int f;
f = (g + h) - (i + j); return f;
}
• Parâmetros g, …, j em $a0, …, $a3
• f em $s0 (obriga a salvar $s0 na pilha)
Exemplo: Chamada de
procedimento “folha”
• Código MIPS: leaf_example: addi $sp, $sp, -4 sw $s0, 0($sp) add $t0, $a0, $a1 add $t1, $a2, $a3 sub $s0, $t0, $t1 add $v0, $s0, $zero lw $s0, 0($sp) addi $sp, $sp, 4 jr $ra Save $s0 on stack Procedure body Restore $s0 Result ReturnProcedimentos não-”folha”
• Procedimentos que chamam outros procedimentos;
• Para uma chamada aninhada, os procedimentos devem salvar na pilha:
• O seu endereço de retorno;
• Quaisquer parâmetros e temporários que serão necessários após a chamada do outro procedimento;
Exemplo: Procedimento
não-”folha”
• Código C:
int fact (int n) {
if (n < 1) return f;
else return n * fact(n - 1); }
• Parâmetros em $a0
•
Código MIPS:
fact:
addi $sp, $sp, -8 # adjust stack for 2 items sw $ra, 4($sp) # save return address
sw $a0, 0($sp) # save argument slti $t0, $a0, 1 # test for n < 1 beq $t0, $zero, L1
addi $v0, $zero, 1 # if so, result is 1
addi $sp, $sp, 8 # pop 2 items from stack jr $ra # and return
L1: addi $a0, $a0, -1 # else decrement n jal fact # recursive call
lw $a0, 0($sp) # restore original n lw $ra, 4($sp) # and return address addi $sp, $sp, 8 # pop 2 items from stack mul $v0, $a0, $v0 # multiply to get result jr $ra # and return
Dados locais na pilha
• Variáveis locais armazenadas pelo procedimento;
• Janela do procedimento (frame):
• Usado por alguns compiladores para gerenciar o armazenamento na pilha
Layout da Memória
• Text: código do programa;
• Static Data: variáveis globais:
• Ex. variáveis estáticas em C, arrays constantes e strings;
• $gp inicializado para tratar endereços nesse segmento;
• Dynamic Data: heap:
• Ex. “malloc” em C, “new” em Java;
Caracteres
• Conjunto de caracteres codificados em bytes:
• ASCII: 128 caracteres:
• 95 gráficos, 33 de controle;
• Latin-1: 256 caracteres:
• ASCII. +96 caracteres gráficos;
• Unicode: conjunto de caracteres de 32 bits:
• Usado em Java, C++, ...
• Maioria dos alfabetos mundiais, mais símbolos;
Operações com byte e
meia-palavra
• Load/store byte/halfword instruções do MIPS:
• Utilizado principalmente para trabalhar com strings;
• lb rt, offset(rs) lh rt, offset(rs)
• Estende o sinal para 32 bits em rt
• lbu rt, offset(rs) lhu rt, offset(rs)
• Estende zero para 32 bits em rt
• sb rt, offset(rs) sh rt, offset(rs)
Exemplo: Copiar String
• Código C:
• String terminada em NULL;
void strcpy (char x[], char y[]) { int i;
i = 0;
while ((x[i]=y[i])!='\0') i += 1;
}
• Endereços de x, y em $a0, $a1;
Exemplo: Copiar String
•
MIPS code:
strcpy:
addi $sp, $sp, -4 # adjust stack for 1 item sw $s0, 0($sp) # save $s0
add $s0, $zero, $zero # i = 0
L1: add $t1, $s0, $a1 # addr of y[i] in $t1 lbu $t2, 0($t1) # $t2 = y[i]
add $t3, $s0, $a0 # addr of x[i] in $t3 sb $t2, 0($t3) # x[i] = y[i]
beq $t2, $zero, L2 # exit loop if y[i] == 0 addi $s0, $s0, 1 # i = i + 1
j L1 # next iteration of loop L2: lw $s0, 0($sp) # restore saved $s0
addi $sp, $sp, 4 # pop 1 item from stack jr $ra # and return
Constantes de 32-bits
• Maioria das constantes são pequenas:
• Imediato de 16-bits é suficiente;
• Para uma constante ocasional de 32-bits: lui rt, constant
• Copia constante de 16 bits para a parte mais significativa do rt
• Zera os 16 bits menos significativos.
0000 0000 0111 1101 0000 0000 0000 0000
lhi $s0, 61
0000 0000 0111 1101 0000 1001 0000 0000
ENDEREÇAMENTO DE
DESVIOS
Endereçamento de desvios
condicionais (branch)
• As instruções de desvio especificam:
• Opcode, dois registradores, endereço alvo;
• Maioria dos desvios são desvios curtos:
• Para cima ou para baixo:
• Endereçamento relativo ao contador de programa (PC):
• Endereço Alvo = PC + (offset x 4);
• OBS: PC já incrementado de 4.
op rs rt constant or address
Endereçamento de desvios
incodicionais (jump)
• Os alvos da instruções de jump (j e jal) podem ser qualquer lugar no segmento de texto:
• O endereço alvo vem codificado na instrução:
• (Pseudo) endereçamento direto:
• Endereço Alvo = PC31...28 : (address x 4)
Concatenação
op address
Exemplo: Endereçamento alvo
• Código de um loop de um exemplo anterior:
• Assuma que o loop está localizado a partir do endereço 80000: Loop: sll $t1, $s3, 2 80000 0 0 19 9 4 0 add $t1, $t1, $s6 80004 0 9 22 9 0 32 lw $t0, 0($t1) 80008 35 9 8 0 bne $t0, $s5, Exit 80012 5 8 21 2 addi $s3, $s3, 1 80016 8 19 19 1 j Loop 80020 2 20000 Exit: … 80024
Desvios condicionais para
áreas distantes
• Se o alvo de um branch é um rótulo muito distante para ser codificado em 16 bits, então o Assembler reescreve o código da seguinte forma:
• Exemplo: beq $s0,$s1, L1 ↓ bne $s0,$s1, L2 j L1 L2: …
Resumo dos modos de
endereçamento
Sincronização
• Dois processadores compartilhando ao mesma região de memória:
• P1 escreve, então P2 lê;
• Condição de corrida de P1 e P2 não sincronizarem:
• Resultado vai depender da ordem dos acessos;
• Necessário suporte de hardware:
• Operação atômica de leitura/escrita na memória;
• Ninguém acessa o dado entre a leitura e a escrita;
• Pode ser uma instrução única:
• Ex. swap atômico entre registrador e memória;
Sincronização no MIPS
• Load Linked: ll rt, offset(rs)
• Store Conditional: sc rt, offset(rs)
• Sucesso se o endereço não mudou desde o ll:
• Retorna 1 em rt;
• Falha se o endereço mudou:
Exemplo: Swap atômico
•
Swap atômico (test/set lock)
try: add $t0,$zero,$s4 ;copy exchange value ll $t1,0($s1) ;load linked
sc $t0,0($s1) ;store conditional beq $t0,$zero,try ;branch store fails
Tradução e Inicialização
Many compilers produce object modules directly
Pseudoinstruções
• Maioria das instruções do Assembler representam instruções de máquina de uma-para-um;
• Pseudoinstruções: fruto da imaginação do Assembler:
move $t0, $t1
→
add $t0, $zero, $t1blt $t0, $t1, L
→
slt $at, $t0, $t1 bne $at, $zero, LProduzindo um Código Objeto
(Partes dos Programas)
• Provê informações para a construção de um programa completo a partir dos pedaços:
• Header: descreve o conteúdo do código objeto;
• Text Segment: instruções traduzidas;
• Static Data Segment: dados alocados durante a vida do programa (variáveis globais);
• Relocation Info: para conteúdos que dependem da localização absoluta do programa carregado;
• Symbol Table: definições globais e referências externas;
“Linkando” Código Objeto
• Produz uma imagem executável:
1. Unifica os segmentos;
2. Resolve os rótulos (determina o seus endereços);
3. Corrige as referências externas e as dependente de localização;
• Pode deixar as dependências de localização para serem corrigidas durante o processo de Loader:
• Com memória virtual, isso não é necessário;
• Programa pode ser carregado em uma localização absoluta no espaço da memória virtual.
Carregando um programa
• Carrega o arquivo de imagem do disco para a memória:
1. Lê o Header para determinar o tamanho dos segmentos;
2. Cria um espaço de endereçamento virtual;
3. Copia instruções e os dados inicializados para a memória:
• Ou configura as entradas da tabela de páginas para que ocorram faltas;
4. Carrega os argumentos na pilha;
5. Inicializa os registradores (incluindo $sp, $fp, $gp);
6. Desvia para a rotina de inicialização:
• Copia os argumentos para $a0, ... e chama a “main”;
“Linkagem” Dinâmica
• Só carrega/linka a biblioteca de procedimentos quando é chamada:
• Necessita que o código do procedimento seja realocável;
• Evita o “inchaço” do executável causado pela linkagem estática de todas as bibliotecas referenciadas;
Lazy Linkage
Indirection table
Stub: Loads routine ID, Jump to linker/loader
Linker/loader code
Dynamically mapped code
Iniciando aplicações Java
Simple portable instruction set for
the JVM Interprets bytecodes Compiles bytecodes of “hot” methods into native code
for host machine
Exemplo: Ordenação em C
• Ilustra o uso de instruções em Assembly para uma função de Bubble Sort em C;
• Procedimento de SWAP (folha):
void swap(int v[], int k) { int temp; temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; }
Procedimento de SWAP
swap: sll $t1, $a1, 2 # $t1 = k * 4 add $t1, $a0, $t1 # $t1 = v+(k*4) # (address of v[k]) lw $t0, 0($t1) # $t0 (temp) = v[k] lw $t2, 4($t1) # $t2 = v[k+1] sw $t2, 0($t1) # v[k] = $t2 (v[k+1]) sw $t0, 4($t1) # v[k+1] = $t0 (temp)Procedimento de ordenação em C
• Não-folha (chama swap):
void sort (int v[], int n) { int i, j; for (i = 0; i < n; i += 1) { for (j = i – 1; j >= 0 && v[j] > v[j + 1]; j -= 1) { swap(v,j); } } } • v em $a0, k em $a1, i em $s0, j em $s1
Corpo do procedimento
move $s2, $a0 # save $a0 into $s2
move $s3, $a1 # save $a1 into $s3 move $s0, $zero # i = 0
for1tst: slt $t0, $s0, $s3 # $t0 = 0 if $s0 ≥ $s3 (i ≥ n)
beq $t0, $zero, exit1 # go to exit1 if $s0 ≥ $s3 (i ≥ n) addi $s1, $s0, –1 # j = i – 1
for2tst: slti $t0, $s1, 0 # $t0 = 1 if $s1 < 0 (j < 0) bne $t0, $zero, exit2 # go to exit2 if $s1 < 0 (j < 0) sll $t1, $s1, 2 # $t1 = j * 4
add $t2, $s2, $t1 # $t2 = v + (j * 4) lw $t3, 0($t2) # $t3 = v[j]
lw $t4, 4($t2) # $t4 = v[j + 1]
slt $t0, $t4, $t3 # $t0 = 0 if $t4 ≥ $t3 beq $t0, $zero, exit2 # go to exit2 if $t4 ≥ $t3
move $a0, $s2 # 1st param of swap is v (old $a0) move $a1, $s1 # 2nd param of swap is j
jal swap # call swap procedure addi $s1, $s1, –1 # j –= 1
j for2tst # jump to test of inner loop exit2: addi $s0, $s0, 1 # i += 1
j for1tst # jump to test of outer loop
Pass params & call Move params Inner loop Outer loop Inner loop Outer loop
Procedimento completo
sort: addi $sp,$sp, –20 # make room on stack for 5 registers sw $ra, 16($sp) # save $ra on stack
sw $s3,12($sp) # save $s3 on stack sw $s2, 8($sp) # save $s2 on stack sw $s1, 4($sp) # save $s1 on stack sw $s0, 0($sp) # save $s0 on stack … # procedure body …
exit1: lw $s0, 0($sp) # restore $s0 from stack lw $s1, 4($sp) # restore $s1 from stack lw $s2, 8($sp) # restore $s2 from stack lw $s3,12($sp) # restore $s3 from stack lw $ra,16($sp) # restore $ra from stack addi $sp,$sp, 20 # restore stack pointer jr $ra # return to calling routine
Efeitos da otimização do
compilador
0 0.5 1 1.5 2 2.5 3 none O1 O2 O3 Relative Performance 0 20000 40000 60000 80000 100000 120000 140000 160000 180000 none O1 O2 O3 Clock Cycles 0 20000 40000 60000 80000 100000 120000 140000 none O1 O2 O3 Instruction count 0 0.5 1 1.5 2 none O1 O2 O3 CPIEfeitos da
linguagem e algoritmo
0 500 1000 1500 2000 2500 3000C/none C/O1 C/O2 C/O3 Java/int Java/JIT
Quicksort vs. Bubblesort Speedup 0 0.5 1 1.5 2 2.5
C/none C/O1 C/O2 C/O3 Java/int Java/JIT
Quicksort Relative Performance 0 0.5 1 1.5 2 2.5 3
C/none C/O1 C/O2 C/O3 Java/int Java/JIT
Lições aprendidas
• Número de instruções e CPI não são bons avaliadores de de desempenho isolados;
• Otimizações do compilador são sensíveis ao algoritmo;
• Código compilado para Java/JIT é significantemente mais rápido que o interpretado pela JVM:
• Comparável com o código C otimizado;
Arrays vs. Ponteiros
• Indexar array envolve:
• Multiplicação do índice pelo tamanho do elemento;
• Adicionar ao endereço base do array;
• Ponteiros corresponde diretamente ao endereço da memória:
Exemplo: Limpando um array
clear1(int array[], int size) { int i;
for (i = 0; i < size; i += 1) array[i] = 0;
}
clear2(int *array, int size) { int *p;
for (p = &array[0]; p < &array[size]; p = p + 1) *p = 0; } move $t0,$zero # i = 0 loop1: sll $t1,$t0,2 # $t1 = i * 4 add $t2,$a0,$t1 # $t2 = # &array[i] sw $zero, 0($t2) # array[i] = 0 addi $t0,$t0,1 # i = i + 1 slt $t3,$t0,$a1 # $t3 = # (i < size) bne $t3,$zero,loop1 # if (…) # goto loop1
move $t0,$a0 # p = & array[0] sll $t1,$a1,2 # $t1 = size * 4 add $t2,$a0,$t1 # $t2 =
# &array[size] loop2: sw $zero,0($t0) # Memory[p] = 0 addi $t0,$t0,4 # p = p + 4 slt $t3,$t0,$t2 # $t3 =
#(p<&array[size]) bne $t3,$zero,loop2 # if (…)
Comparação entre Array e
Ponteiro
• Multiplicar a “força reduzida” do uso do shift;
• Versão com array necessita que os shifts estejam dentro do loop;
• Parte do cálculo do índice para o i incrementado;
• Compilador por alcançar o mesmo efeito com o uso manual de ponteiros:
• Eliminação da variável de indução;
Similaridades ARM & MIPS
• ARM: processador embarcado mais popular;
• Conjunto de instruções similar ao do MIPS;
ARM MIPS
Date announced 1985 1985
Instruction size 32 bits 32 bits
Address space 32-bit flat 32-bit flat
Data alignment Aligned Aligned
Data addressing modes 9 3
Registers 15 × 32-bit 31 × 32-bit
Input/output Memory
mapped
Memory mapped
Comparação e Desvio no
ARM
• Usa códigos condicionais para resultado de uma operação lógica/aritmética;
• Negativo, zero, carry, overflow;
• Compara instruções para mudar os código de condição sem manter o resultado;
• Cada instrução pode ser condicional:
• 4 bits mais significativos da instrução: valor da condição;
Intel x86 ISA
• Evolução com compatibilidade:
• 8080 (1974): 8-bit microprocessor
• Accumulator, plus 3 index-register pairs
• 8086 (1978): 16-bit extension to 8080
• Complex instruction set (CISC)
• 8087 (1980): floating-point coprocessor
• Adds FP instructions and register stack
• 80286 (1982): 24-bit addresses, MMU
• Segmented memory mapping and protection
• 80386 (1985): 32-bit extension (now IA-32)
• Additional addressing modes and operations
Intel x86 ISA
• ...
• i486 (1989): pipelined, on-chip caches and FPU
• Compatible competitors: AMD, Cyrix, …
• Pentium (1993): superscalar, 64-bit datapath
• Later versions added MMX (Multi-Media eXtension) instructions
• The infamous FDIV bug
• Pentium Pro (1995), Pentium II (1997)
• New microarchitecture (see Colwell, The Pentium Chronicles)
• Pentium III (1999)
• Added SSE (Streaming SIMD Extensions) and associated registers
• Pentium 4 (2001)
• New microarchitecture
Intel x86 ISA
• ...
• AMD64 (2003): extended architecture to 64 bits • EM64T – Extended Memory 64 Technology (2004)
• AMD64 adopted by Intel (with refinements)
• Added SSE3 instructions
• Intel Core (2006)
• Added SSE4 instructions, virtual machine support
• AMD64 (announced 2007): SSE5 instructions
• Intel declined to follow, instead…
• Advanced Vector Extension (announced 2008)
• Longer SSE registers, more instructions
•
Se a Intel não estender a compatibilidade, seus
concorrentes irão!
Modos de endereçamento x86
• Dois operandos por instrução:
• Modos de endereçamento da memória:
• Address in register
• Address = Rbase + displacement
• Address = Rbase + 2scale × Rindex (scale = 0, 1, 2, or 3)
• Address = Rbase + 2scale × Rindex + displacement
Source/dest operand Second source operand Register Register
Register Immediate Register Memory
Memory Register Memory Immediate
Codificação das instruções
do x86
• Tamanho da codificação variável: • Sufixo especifica o modo de endereçamento; • Prefixo modifica a operação: • Tamanho do operado, repetição, travamento, ...Implementando IA-32
• Instruções complexas fazem a implementação difícil:
• Hardware traduz instruções para microoperações mais simples:
• Instruções simples: 1 – 1;
• Instruções complexas: 1 – muitos;
• Microengine similar a um RISC;
• Marke share tornou isto economicamente viável;
• Desempenho comparável ao RISC:
Falácias
• Instruções poderosas alto desempenho:
• Poucas instruções necessárias;
• Instruções complexas são difíceis de implementar:
• Podem deixar lenta todas as instruções, incluindo as simples;
• Compiladores são bons em fazer código rápido com instruções simples;
• Uso de código Assembly para alto desempenho:
• Compiladores modernos conseguem lidar melhor com processadores modernos;
• Mais linhas de código mais erros e menos produtividade
Falácias
• Compatibilidade conjunto de instruções não muda:
• Eles acrescentam mais instruções.
Armadilhas
• Palavras sequenciais não são endereços sequenciais:
• Endereços são incrementados por 4 e não por 1!
• Mantendo um ponteiro para um variável local depois que o processo retorna:
• Ex. passando o ponteiro de volta via um argumento;
Relembrando
• Princípios de projeto:
1. Simplicidade favorece regularidade;
2. Menor é mais rápido;
3. Faça o caso comum mais rápido;
4. Bons projetos demandam bons compromissos;
• Camadas de software/hardware:
• Compilador, Assembler, hardware;
• MIPS: típico ISA dos RISCs:
Relembrando
• Meça a execução das instruções do MIPS em programas de benchmarks:
• Considere fazendo o caso comum mais rápido;
• Considere bons compromissos.
Instruction class MIPS examples SPEC2006 Int SPEC2006 FP Arithmetic add, sub, addi 16% 48%
Data transfer lw, sw, lb, lbu, lh, lhu, sb, lui
35% 36%
Logical and, or, nor, andi, ori, sll, srl
12% 4%
Cond. Branch beq, bne, slt, slti, sltiu
34% 8%