Modulo VI
Escrevendo código em
Linguagem de Montagem
(Assembly)
Conjunto de Instruções
 Arquitetura RISC
 Reduced Instruction Set Computer
 Operação com dados se dão nos registradores, nunca na
memória
 Instruções de carga (Load) de dados da memória em
registradores
 Instruções de armazenagem (Store) de dados de
registradores para a memória
Formato Típico das Instruções Assembly
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
 Itens entre { } são opcionais
 Itens entre < > são uma breve descrição sobre um
tipo de valor que será passado pelo programador
 Muitas instruções podem ser executadas
condicionalmente
 Isso pode evitar os desvios (branch). Desvios
podem prejudicar o desempenho de arquiteturas
com muitos níveis no pipeline
Formato Típico das Instruções Assembly
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
 Muitas instruções podem alterar ou não os bits de
estados dependendo da forma da instrução
 Facilita a comparação de dados (pos, neg, zero..)
 Operações aritméticas em dados não relacionados
podem ocorrer durante uma pendência, sem alterar
os bits de estados
 O último operando é um operador de deslocamento,
outros são registradores.
Formato Típico das Instruções Assembly
Em C
if (a > b)
c = a;
else
c = b;
Considere que R0, R1
e R2 estão carregados
com os valores de a, b
e c, respectivamente
Em Assembly
cmp
ble
mov
b
R0, R1
L1
R2, R0
L2
mov
R2, R1
L1:
L2:
ou
cmp R0, R1
movgt R2, R0
movle R2, R1
Formato do operador_de_deslocamento
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
 #<immediate> - Valor pode receber somente certos
valores
 <Rm> - Conteúdo de Rm
 <Rm>, LSL #<shift_imm> - Conteúdo de Rm deslocado
logicamente à esquerda por um valor imediato,
exemplo: R2, LSL #5
 <Rm>,LSL <Rs> - Conteúdo de Rm é deslocado
logicamente à esquerda por um valor contido em Rs
Formato do operador_de_deslocamento
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
 <Rm>, LSR #<shift_imm> - Conteúdo de Rm é
deslocado logicamente à direita por um valor imediato
 <Rm>, LSR <Rs> - Conteúdo de Rm é deslocado
logicamente à direita por um valor contido em Rs
 <Rm>, ASR #<shift_imm> - Conteúdo de Rm é
deslocado para direita aritmeticamente por um valor
imediato, com ou sem sinal
 <Rm>, ASR <Rs> - Conteúdo de Rm é deslocado
aritmeticamente à direita por um valor contido em Rs,
com ou sem sinal
Formato do operador_de_deslocamento
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
 <Rm>, ROR #<shift_imm> - Conteúdo de Rm é
rotacionado à direita por um valor imediato
 <Rm>, ROR <Rs> - Conteúdo de Rm é rotacionado à
direita por uma quantidade Rs
 <Rm>, RRX – Conteúdo de Rm é deslocado um bit à
direita, o bit de Carry é deslocado para alto e o bit 0 de
Rm é colocado no bit de carry
As operações de deslocamento não mudam o conteúdo de
Rm, apenas o valor do resultado usado como o operando
Operador de deslocamento: Formas do
ROR 4 bits
Imediato 8 bits
imediato
 Pode rotacionar à direita um valor imediato de 8 bits por qualquer
número par de bits
 O campo de rotação tem seu valor multiplicado por dois, dessas
forma é possível deslocar até 30 bits
 Apenas valores especiais de constantes podem ser representados
 0xFF (possível)
 0xFF0 (possível)
 0xFF1 (Não)
 0x1FE (Não)
 Pode ser necessário formar constantes em mais de uma etapa, por
exemplo: 0xFFFF
 Evite, se for possível, criar espaços para dados constantes
Formato do operador_de_deslocamento
#<immediate>
mov
add
mov
ldr
R0, #127
R0, R1, #0x7f
R2, #125952
R2, =125951
;R0 = 127
;R0 = R1 + 127
;R2 = 125952 = 123*4*4*4*4*4
;R2 = 125951 = 123*4*4*4*4*4 - 1
LDR R2, =125951 é uma instrução que é desdobrada
pelo montador em um carregamento (LDR) de uma
posição de memória com o valor 125951
mov
add
mov
R0, #257
R0, R1, #0x101
R2, #125951
;erro!
;erro!
;125951 = 123*4*4*4*4*4-1 erro!
Formato típico de uma instrução LDR/STR
LDR{<cond>} <Rd>, <modo_de_endereçamento>
 LDR - Usado para carregar um valor da memória para
um registrador
 STR - Usado para armazenar o valor em um registrador
na memória
 Pode ser executado condicionalmente
 Não altera os bits de estados CSPR (Current Status
Program Register)
 Usa modo_de_endereçamento para endereçar o dado
na memória
Formato do modo_de_endereçamento
LDR{<cond>} <Rd>, <modo_de_endereçamento>
 [<Rn>,#+/-<offset_12>] – O endereço é o conteúdo de
Rn mais ou menos o offset_12 de 12 bits com sinal
 [<Rn>,+/-<Rm>] – Endereço é o conteúdo de Rm mais
ou menos o conteúdo de Rm
 [<Rn>,+/-<Rm>, <shift> <shift_imm>] – O endereço é o
conteúdo de Rn mais ou menos conteúdo de Rm
deslocado por um operador <shift> pelo número de
bits em <shift_imm>
 [<Rn>,#+/-<offset_12>]! – O endereço é o conteúdo de
Rn mais ou menos um offset_12 de 12 bits com sinal,
endereço é escrito de volta para Rn (pré-indexado)
Formato do modo_de_endereçamento
LDR{<cond>} <Rd>, <modo_de_endereçamento>
 [<Rn>, +/- <Rm>]! – O endereço é o conteúdo de Rn
mais ou menos o conteúdo de Rm, endereço é escrito
de volta para Rn (pré-indexado)
 [<Rn>, +/-<Rm>, <shift> <shift_imm>]! – O endereço é
o conteúdo de Rn mais ou menos o conteúdo de Rm
deslocado por um operador <shift> pelo número de
bits em <shift_imm>, o endereço é escrito de volta
para Rn (pré-indexado)
 [<Rn>], #+/-<offset_12> - O endereço é o conteúdo de
Rn, o valor offset_12 de 12 bits com sinal é adicionado
a Rn, escrito de volta para Rn (pós-indexado)
Formato do modo_de_endereçamento
LDR{<cond>} <Rd>, <modo_de_endereçamento>
 [<Rn>] +/-<Rm> - O endereço é o conteúdo de Rn, Rm
é adicionado ou subtraído de Rn, escrito de volta para
Rn (pós-indexado)
 [<Rn>] +/-<Rm>, <shift> <shift_imm> - O endereço é o
conteúdo de Rn, conteúdo de Rm é deslocado
operador <shift> pelo número de bits em <shift_imm>
o valor é adicionado ou subtraído de Rn (pósindexado)
Formato do modo_de_endereçamento
;Coloca o valor apontado por R0 em R1 e incrementa R0 para
;indicar o próximo inteiro de 32 bits
ldr
R1, [R0], #4
;Incrementa o conteúdo de R2 fazendo-o apontar para o próximo
;inteiro de 32 bits, esse inteiro é atribuído para R3
ldr
R3, [R2, #4]!
;O endereço é o valor R5 deslocado 4 bytes, o resultado aponta
;para o próximo inteiro de 32 bits, esse inteiro é atribuído para R4.
;R5 não é alterado
ldr
R4, [R5, #4]
;O mesmo endereçamento pode ser usado pela instrução STR
str
R1, [R0], #4
str
R3, [R2, #4]!
str
R4, [R5, #4]
Instruções Dignas de Atenção
 BIC – Bit Clear
 desliga os bits indicados na mascara
 CLZ – Count Leading Zeros
 ideal para determinar o número de bits necessários para
representar um número
 LDM/STM – Load/Store Multiple
 usado para salvar e recuperar registradores na pilha
 especialmente útil: STMDB, LDMDB
 RSB – Reverse Substract Operand Order
 permitem que o operador_de_deslocamento seja subtraídos do
registrador
Registradores
 R0 até R3 – Propósito geral, salvos pelo chamador da
rotina
 R4 até R11 – Propósito geral, salvos pela rotina chamada
 R12 – Frame Pointer (algumas vezes)
 R13 – Stack Pointer (SP – Apontador da Pilha)
 R14 – Link Register (LR)
 R15 – Program Counter (PC)
 CSPR – Currente Status Program Register, contém a
sinalização de overflow, zero, underflow, ...
Registradores
 Pode ser desejável dar nomes mais sugestivos aos registradores,
para isso usa-se a diretiva RN
contador RN
R0
resultado RN
R1
 Pode ser desejável também colocar nomes as constantes literais
(valores imediatos)
mascara
EQU
0x000000ff
minimo
EQU
0
maximo
EQU
32767
 Usando:
mov
contador, #minimo
cmp
contador, #maximo
Convenção da Pilha
 A Pilha cresce “para baixo”
Topo
SP
 Dados colocados na pilha têm
endereços de memória menores
que os dados colocados
previamente
SP + 4
 O armazenamento dos dados na
pilha é pré-decrementado.
SP + 12
 Ponteiro da pilha aponta para o
último elemento colocado nela.
SP + 8
SP + 16
Crescimento
da pilha
Assembly: Estrutura do Arquivo
;cpenable.asm
Comentários se
iniciam com ;
Informações sobre a
área que será usada
AREA CPENABLE, CODE, READONLY
EXPORT é usado para
exportar o nome da função
para uso externo
Rótulos começam na
coluna 1, PROC é uma
diretiva usada para
identificar funções
;CP15 Routines
EXPORT GetCP0Enable
; Retorna valor escrito em r0
GetCP0Enable PROC
MRC p15, 0x0, r0, c15, c1, 0x0
mov
pc,lr
ENDP
END
END indica onde o
arquivo acaba
ENDP mostra onde a
função acaba
Chamando um Módulo em Assembly
 Por convenção no C para o ARM os quatro primeiros parâmetros são
passados nos registradores R0 – R3
 O restante é passado na pilha, da esquerda para a direita, sendo que
o parâmetro mais à esquerda está no topo da pilha
 O registrador Stack Pointer aponta para o 5º parâmetro se ele estiver
presente
 Ajustes são necessários se a função não for uma função folha
 Para funções folha, podemos usar o stack pointer tanto para pilha
quanto para frame pointer
 Para funções não folha o stack pointer será ajustado, ou usa-se R12
como frame pointer ou ajusta-se as referências das variáveis colocadas
na pilha pelo chamador
Exemplo da Manipulação de Parâmetros em
uma função Asm
Parâmetros de a-d
estão em R0-R3,
respectivamente
O parâmetro e é o
mais baixo na pilha, o
ponteiro da pilha
stack pointer aponta
para ele. Agora, ele
está em R4.
; parms.asm
AREA PARMS, CODE, READONLY
;Routines
Salva os
EXPORT Parms
;Parms (a,b,c,d,e,f)
registradores R4
Parms PROC
e R5 na pilha
stmdb sp, {r4,r5}
ldr
r4, [sp]
ldr
r5, [sp, #4]
Parâmetro f é o
; o código vai aqui
ldmdb sp, {r4,r5}
próximo na pilha,
mov
pc,lr
assim, stack
ENDP
pointer+4 é
END
atribuido para R5
Retornando da Função em Assembly
Restaura os valores de
R4 e R5, ajuste o stack
pointer se necessário
Restaura o PC com o
conteúdo do Link Register.
Lembre-se de salvar o
conteúdo de LR se esta
função não for uma função
folha
;count.asm
AREA PARMS, CODE, READONLY
;Routines
EXPORT Count
Count PROC
stmdb sp, {r4,r5}
mov
r4, #0
mov
r5, #0xF8
loop
Valores de
add
r4,r4,#1
retorno da
cmp
r4,r5
blt
loop
função são
passados em R0
mov
r0,r4
ldmdb sp, {r4,r5}
mov
pc,lr
ENDP
END
Exemplo da Manipulação de Parâmetros em
uma função Asm
Soma os dois parâmetros e retorna o resultado pela função
;soma2.asm
AREA SOMA, CODE, READONLY
;Routinas
EXPORT soma2
;int soma2(int, int)
soma2 PROC
add
R0, R0, R1
;R0 = R0 + R1
mov
pc,lr
ENDP
END
Retornando da Função em Assembly
Soma os seis
parâmetros e
retorna o resultado
pela função
;soma6.asm
AREA SOMA, CODE, READONLY
;Routinas
EXPORT soma6
;int soma6(int, int, int, int, int, int)
soma6
PROC
stmdb
add
add
add
ldr
ldr
add
add
ldmdb
mov
ENDP
END
SP, {R4,R5}
R0, R0, R1
R1, R2, R3
R0, R0, R1
R4, [SP]
R5, [SP, #4]
R1, R4, R5
R0, R0, R1
SP, {R4,R5}
PC, LR
Retornando da Função em Assembly
Soma os seis
parâmetros e retorna
o resultado no sétimo
parâmetro
Observe que os
registradores R4 e
R5 não estão mais
sendo usados.
;soma7.asm
AREA SOMA, CODE, READONLY
;Routinas
EXPORT soma7
;int soma7(int, int, int, int, int, int, int *)
soma7
PROC
add
add
add
ldr
ldr
add
add
ldr
str
mov
ENDP
END
R0, R0, R1
R1, R2, R3
R0, R0, R1
R1, [SP]
R2, [SP, #4]
R1, R1, R2
R0, R0, R1
R1, [SP, #8]
R0, [R1]
PC, LR
Retornando da Função em Assembly
AREA SOMA, CODE, READONLY
EXPORT soma7
;int soma7(int, int, int, int, short int, short int, short int *)
soma7
Soma os seis
parâmetros e
retorna o resultado
no sétimo parâmetro
PROC
stmdb
add
add
add
ldrh
ldrh
add
add
ldr
strh
ldmdb
mov
ENDP
END
SP, {R4, R5, R6}
R0, R0, R1
R1, R2, R3
R0, R0, R1
R4, [SP]
R5, [SP, #4]
R1, R4, R5
R0, R0, R1
R5, [SP, #8]
R0, [R5]
SP, {R4, R5, R6}
PC, LR
Observe que os
três últimos
parâmetros têm
16 bits cada
Passando vetor por parâmetro em Assembly
Retorna o endereço de um elemento em um vetor
int vet[10]; int p*;
int * procura(int v[ ], int x) {
int i;
for (i=0; i<10; i++)
if (v[i]==x)
return &v[i];
return NULL;
}
void main(void) {
..
.
p=procura(vet, 1234);
..
}
procura
AREA VETOR, CODE, READONLY
EXPORT procura
PROC
mov
R2, #0
laco
ldr
cmp
addeq
moveq
add
cmp
movge
movge
b
ENDP
END
R3, [R0, R2, LSL #2]
R1, R3
R0, R0, R2
PC, LR
;return &v[i]
R2, #1
R2, #10
R0, #0
;return NULL
PC, LR
laco
Passando ponteiro por parâmetro em Assembly
Iniciação de uma lista linear
simplesmente encadeada
typedef struct s_no {
long
dado;
struct s_no
*prox;
} no;
&lista, d R0
d R0
lista, *d [R0]
?
antes
*d [R0]
depois
no *lista;
void iniciaLista(no
*d=NULL;
}
void main(void) {
..
.
iniciaLista(&lista)
..
.
}
**d) {
AREA LISTA, CODE, READONLY
EXPORT iniciaLista
iniciaLista
PROC
mov
R1, #0
str
R1, [R0]
mov
PC, LR
ENDP
END
Exportando uma função Assembly de
uma DLL
AREA .drectve DRECTVE
DCB “-export:Count”
DCB é usado para definir
uma string na qual contém
diretivas para o ligador
ajustar o código como sendo
uma exportação .dll
A diretiva deve estar na
área DRECTVE em uma
seção chamada .drectve
;count.asm
AREA COUNT,CODE,READONLY
; Rotinas
EXPORT Count
Count PROC
stmdb
sp, {r4,r5}
mov
r4, #0
mov
r5, #0xF8
loop
add
r4, r4,#1
cmp
r4, r5
blt
loop
mov
r0, r4
ldmdb
sp, {r4,r5}
mov
pc, lr
ENDP
END
Usando Assembly na IDE
 Clique na pasta Source files, adicione um arquivo .asm no projeto
 Selecione ‘All Files’ na caixa de diálogo, assim você pode ver os
arquivos .asm
 Clique com o botão direito no
arquivo ASM na pasta source do
projeto, vá para settings
 Na aba Custom Build
 Digite armasm para chamar o
montador
 Utilize a opção xscale cpu se
estiver chamando mnemônicos
xscale (armasm –cpu xscale)
 Use o caminho relativo ao arquivo
como o apresentado no arquivo
de entrada (input File)
 Use apenas o nome do arquivo
com a extensão .obj como saída
Melhorando as Dependências Usando LDR
add r1, r2, r3
ldr r0, [r5]
add r6, r0, r1
sub r8, r2, r3
mul r9, r2, r3
ldr r0, [r5]
add r1, r2, r3
sub r8, r2, r3
add r6, r0, r1
mul r9, r2, r3
Com o dado na Cache, três ciclos
são necessários para a transferência
do valor ao registrador
Como o dado pode
não estar na cache, o
LDR deve ser
colocado o mais cedo
possível antes do
ponto de utilização
Instruções LDRD/STRD
 Lê e armazena 64 bits em dois registradores
 O Endereço deve estar alinhado em 8 bytes (64bits)
 Os registradores devem ser os pares:
 R0,R1; R2,R3; R4,R5; ...
add
sub
ldrd
orr
mul
r6,
r5,
r0,
r8,
r7,
r7,
r6,
[r3]
r1,
r0,
r8
r9
#0xf
r7
ldrd
add
sub
mul
orr
r0,
r6,
r5,
r7,
r8,
[r3]
r7,
r6,
r0,
r1,
r8
r9
r7
#0xf
LDRD necessita de 3 ciclos para disponibilizar
os dados nos registradores
Instruções LDM/STM
 2 a 20 ciclos para disponibilizar os valores nos
registradores,
 depende do número de registradores envolvidos na troca de dados
 A latência é de 2 ciclos mais 1 ciclo por registrador
envolvido
 A próxima instrução é atrasada um ciclo (com ou sem
dependências e acessos à memória)
Utilizando LDM ou LDRD
 Soma de dois valores de 64 bits
 r0 endereço do primeiro valor
 r1 endereço do segundo valor
ldm r0, {r2, r3}
ldm r1, {r4, r5}
adds r0, r2, r4
adc r1, r3, r5
ldrd r2, [r0]
ldrd r4, [r1]
adds r0, r2, r4
adc r1, r3, r5
Mais rápido para executar
Indo além do básico em Assembly
 Estude o ARM ARM (ARM Architecture Reference
Manual), também disponível no formato pdf em
www.arm.org
 Estude o help do EVC++, tópico ARM Assembly
 Compile seu código com a opção de disassembly
ativada e veja o que o compilador gera:
 Misture código C com assembly
 Desligue as otimizações do compilador
Indo além do básico em Assembly: Use o
eVC++
Vá em Project Settings->
C/C++ tab -> Selecione
Listing Files
Coloque List
Assembly with
Source
Quando e onde substituir o código por Assembly
 Apenas em regiões do código críticas no desempenho
 Para tirar maior proveito do pipeline do processador
Intel® XScale™
 O número de variáveis envolvidas for grande
 O compilador é incapaz de fazer um bom uso de muitos
registradores
 O compilador está armazenando valores locais em
memória
 Quando esses valores poderiam ser mantidos nos registradores
 Há instruções muito melhores em Assembly para:
 Melhor acesso a overflow
 Melhor acesso a instruções DSP
Quando for escrever o código em Assembly
 Utilize muitos comentários
 Inclua o código em C, que o Assembly estiver
substituindo, como comentário
 Use as diretivas RN e EQU para dar nomes
esclarecedores aos registradores e aos valores imediatos
 Sempre salve e recupere os conteúdos dos Registradores
usados pela função que faz a chamada se você os usa
 Cuidado ao trabalhar com funções que não são do tipo
folha
 Salve o conteúdo de LR
 Observe as alterações em SP
Quando for escrever o código em Assembly
 Tome cuidado com o alinhamento de dados
 Para ler dados do tipo short em um vetor use as
instruções LDRH ou LDRSH
 Use STRH para escrever dados do tipo short em um vetor
 Assim como com os tipos short, tenha certeza de estar
usando LDRB para vetores de bytes
 Desconsiderar essas instruções para dados do tipo short
ou byte pode causar problemas de alinhamento de dados
Erros de alinhamento em geral causam o travamento da
máquina, deixando-o sem pistas do que pode ter dado errado
com o seu programa
Otimizando Código em Assembly
 Não armazena dados de volta a pilha
 Mantenha as variáveis em registradores o máximo de tempo
possível
 Observe a tabela de latências
 Ordene as instruções tendo em mente o tempo de latência de
cada uma delas
 Tente desenrolar o laço
 Tente usar PreLoad também em Assembly (Instrução PLD)
 Lembre-se que algumas instruções aritméticas levam mais de 1
ciclo de clock
 Não deixe de tirar proveito das instruções de execução condicional
 Repita pequenos pedaços de código se isso conseguir evitar desvios
(branches)
Modulo VI
Técnicas de Otimização
Considerações sobre o código Compilado
Evite o uso desnecessário de Shorts
int Test(void)
{
short x, y;
x = 3;
y = 4;
Observe que o compilador
realiza dois deslocamentos
para estender o sinal do
valor de ShortSum para
retornar o valor de Test.
Perda de tempo!
; depois de retornar de ShortSum
mov r1,r0,lsl #16
mov r0,r1,#asr #16
mov pc,lr
ENDP
ShortSum PROC ; start
return ShortSum(x, y);
}
mov r0,r0,lsl #16
mov r2,r0,asr #16
mov r1,r1,lsl #16
short ShortSum(short a, short b)
add r0,r2,r1,asr #16
{
mov r2,r0,lsl #16
return a+b;
}
Diversos mov’s para truncar e
estender o sinal dos
operandos e do resultado
mov r0,r2,asr #16
mov pc,lr ; return
ENDP
Corrija as suas variáveis shorts
int Test(void)
{
int x,y;
Se necessário manter
o mesmo range de
short, use cast
;Depois de retornar de ShotSum
mov r1,r0,lsl #16
mov r0,r1,#asr #16
x = 3;
mov pc,lr
y = 4;
ENDP
return (short)ShortSum(x, y);
}
ShortSum PROC ; start
add r0,r0,r1
int ShortSum(int a, int b)
{
mov pc,lr ; return
return a+b;
}
ENDP
Evite o Apelidamento de Variáveis
[Aliasing of Variables]
int AliasTaken(int indx)
{
TransIndex(&indx);
indx += 5;
indx += DisplaceIndex(indx);
indx += DitherIndex(indx);
return indx;
}
Já que usamos o
endereço de indx, o
compilador é
conservador em mantê-lo
em um registrador,
causando vários acessos
à memória
; TransIndex(&indx)
add r0, sp,#8
bl TransIndex
; indx += 5;
ldr r3, [sp, #8]
add r0, r3, #5
str r0, [sp, #8]
;indx += DisplaceIndex(indx);
bl
DisplaceIndex ; 00000028
mov r3, r0
ldr r0, [sp, #8]
add r0, r0, r3
str
r0, [sp, #8]
; indx += DitherIndex(indx);
bl DitherIndex ; 0000003C
mov r3, r0
ldr r0, [sp, #8]
add r0, r0, r3
Aperfeiçoando o Código
int AliasTaken(int indx)
; ref = indx;
mov r4, r0
; TransIndex(&ref);
{
add r0, sp, #0
inf ref = indx;
str
r4, [sp]
TransIndex(&ref);
bl
TransIndex
indx += 5;
indx += DisplaceIndex(indx);
; indx += 5;
add r4, r4, #5
indx += DitherIndex(indx);
; indx += DisplaceIndex(indx);
return indx;
mov
r0, r4
bl
DisplaceIndex ; 00000028
add
r4, r4, r0
}
Observe que a variável
indx é mantida agora em
r4, sem exigir repetidos
STR/LDR para [sp, #0]
; indx += DitherIndex(indx);
mov
r0, r4
bl
DitherIndex ; 00000034
add
r0, r0, r4
Módulo VI
Laboratório
Download

ENDP