Modulo VI Escrevendo código em Linguagem de Montagem (Assembly)
Transcript of Modulo VI Escrevendo código em Linguagem de Montagem (Assembly)
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
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
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
Formato Típico das Instruções Assembly
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.
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
Formato Típico das Instruções Assembly
cmp R0, R1ble L1mov R2, R0b L2
L1:mov R2, R1
L2:
if (a > b) c = a;else c = b;
Em C Em Assembly
cmp R0, R1movgt R2, R0movle R2, R1
ouConsidere que R0, R1 e R2 estão carregados com os valores de a, b e c, respectivamente
Formato do 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
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
Formato do 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
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
Formato do 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
MOV{<cond>}{S} <Rd>, <operador_de_deslocamento>
Operador de deslocamento: Formas do 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
ROR 4 bits Imediato 8 bits
Formato do operador_de_deslocamento
#<immediate>
mov R0, #127 ;R0 = 127add R0, R1, #0x7f ;R0 = R1 + 127mov R2, #125952 ;R2 = 125952 = 123*4*4*4*4*4ldr R2, =125951 ;R2 = 125951 = 123*4*4*4*4*4 - 1
mov R0, #257 ;erro!add R0, R1, #0x101 ;erro!mov R2, #125951 ;125951 = 123*4*4*4*4*4-1 erro!
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
Formato típico de uma instrução LDR/STR
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
LDR{<cond>} <Rd>, <modo_de_endereçamento>
Formato do 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)
LDR{<cond>} <Rd>, <modo_de_endereçamento>
Formato do 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)
LDR{<cond>} <Rd>, <modo_de_endereçamento>
Formato do 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ós-indexado)
LDR{<cond>} <Rd>, <modo_de_endereçamento>
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], #4str 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
minimoEQU 0
maximo EQU 32767
Usando:
mov contador, #minimo
cmp contador, #maximo
Convenção da Pilha A Pilha cresce “para baixo”
Dados colocados na pilha têm endereços de memória menores que os dados colocados previamente
O armazenamento dos dados na pilha é pré-decrementado.
Ponteiro da pilha aponta para o último elemento colocado nela.
SP + 16
SP + 12
SP + 8
SP + 4
SP
Crescimento da pilha
Topo
Assembly: Estrutura do Arquivo;cpenable.asm
AREA CPENABLE, CODE, READONLY;CP15 Routines
EXPORT GetCP0Enable
; Retorna valor escrito em r0GetCP0Enable PROC
MRC p15, 0x0, r0, c15, c1, 0x0mov pc,lrENDPEND
Comentários se iniciam com ;
Informações sobre a área que será usada
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
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
; parms.asm
AREA PARMS, CODE, READONLY;Routines
EXPORT Parms;Parms (a,b,c,d,e,f)Parms PROC
stmdb sp, {r4,r5}ldr r4, [sp]ldr r5, [sp, #4]
; o código vai aquildmdb sp, {r4,r5}mov pc,lrENDPEND
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.
Parâmetro f é o próximo na pilha,
assim, stack pointer+4 é
atribuido para R5
Salva os registradores R4
e R5 na pilha
Retornando da Função em Assembly;count.asm
AREA PARMS, CODE, READONLY;Routines
EXPORT Count Count PROC
stmdb sp, {r4,r5}mov r4, #0mov r5, #0xF8
loopadd r4,r4,#1cmp r4,r5blt loop
mov r0,r4ldmdb sp, {r4,r5}mov pc,lrENDPEND
Valores de retorno da função são
passados em R0
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
Exemplo da Manipulação de Parâmetros em uma função Asm
;soma2.asm
AREA SOMA, CODE, READONLY;Routinas
EXPORT soma2
;int soma2(int, int)soma2 PROC
add R0, R0, R1 ;R0 = R0 + R1mov pc,lrENDPEND
Soma os dois parâmetros e retorna o resultado pela função
Retornando da Função em Assembly;soma6.asm
AREA SOMA, CODE, READONLY;Routinas
EXPORT soma6
;int soma6(int, int, int, int, int, int)soma6 PROC
stmdb SP, {R4,R5}add R0, R0, R1add R1, R2, R3add R0, R0, R1ldr R4, [SP]ldr R5, [SP, #4]add R1, R4, R5add R0, R0, R1ldmdb SP, {R4,R5}mov PC, LRENDPEND
Soma os seis parâmetros e
retorna o resultado pela função
Retornando da Função em Assembly;soma7.asm
AREA SOMA, CODE, READONLY;Routinas
EXPORT soma7
;int soma7(int, int, int, int, int, int, int *)soma7 PROC
add R0, R0, R1add R1, R2, R3add R0, R0, R1ldr R1, [SP]ldr R2, [SP, #4]add R1, R1, R2add R0, R0, R1ldr R1, [SP, #8]str R0, [R1]mov PC, LRENDPEND
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.
Retornando da Função em Assembly
Soma os seis parâmetros e
retorna o resultado no sétimo parâmetro
Observe que os três últimos
parâmetros têm 16 bits cada
AREA SOMA, CODE, READONLYEXPORT soma7
;int soma7(int, int, int, int, short int, short int, short int *)soma7 PROC
stmdb SP, {R4, R5, R6}add R0, R0, R1add R1, R2, R3add R0, R0, R1ldrh R4, [SP]ldrh R5, [SP, #4]add R1, R4, R5add R0, R0, R1ldr R5, [SP, #8]strh R0, [R5]ldmdb SP, {R4, R5, R6}mov PC, LRENDPEND
Passando vetor por parâmetro em Assembly
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); . . }
Retorna o endereço de um elemento em um vetorAREA VETOR, CODE, READONLYEXPORT procura
procura PROCmov R2, #0
lacoldr R3, [R0, R2, LSL #2] cmp R1, R3addeq R0, R0, R2moveq PC, LR ;return &v[i]add R2, #1cmp R2, #10movge R0, #0 ;return NULLmovge PC, LRb lacoENDPEND
Passando ponteiro por parâmetro em Assembly
typedef struct s_no { long dado; struct s_no *prox;} no;
no *lista;
void iniciaLista(no **d) { *d=NULL;}
void main(void) { . . . iniciaLista(&lista) . . .}
Iniciação de uma lista linear simplesmente encadeada
AREA LISTA, CODE, READONLYEXPORT iniciaLista
iniciaLista PROCmov R1, #0str R1, [R0]mov PC, LRENDPEND
&lista, d R0
lista, *d [R0]
d R0
*d [R0]
? antes depois
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
AREA .drectve DRECTVEDCB “-export:Count”
;count.asmAREA COUNT,CODE,READONLY
; RotinasEXPORT Count
Count PROCstmdb sp, {r4,r5}mov r4, #0mov r5, #0xF8
loopadd r4, r4,#1cmp r4, r5blt loopmov r0, r4ldmdb sp, {r4,r5}mov pc, lrENDPEND
Exportando uma função Assembly de uma DLL
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
ldr r0, [r5]
add r1, r2, r3
sub r8, r2, r3
add r6, r0, r1
mul r9, r2, r3
add r1, r2, r3
ldr r0, [r5]
add r6, r0, r1
sub r8, r2, r3
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 r6, r7, r8
sub r5, r6, r9
ldrd r0, [r3]
orr r8, r1, #0xf
mul r7, r0, r7
ldrd r0, [r3]
add r6, r7, r8
sub r5, r6, r9
mul r7, r0, r7
orr r8, r1, #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
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
Soma de dois valores de 64 bits r0 endereço do primeiro valor r1 endereço do segundo valor
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;
return ShortSum(x, y);
}
short ShortSum(short a, short b)
{
return a+b;
}
; depois de retornar de ShortSum
mov r1,r0,lsl #16
mov r0,r1,#asr #16
mov pc,lr
ENDP
ShortSum PROC ; start
mov r0,r0,lsl #16
mov r2,r0,asr #16
mov r1,r1,lsl #16
add r0,r2,r1,asr #16
mov r2,r0,lsl #16
mov r0,r2,asr #16
mov pc,lr ; return
ENDP
Observe que o compilador Observe que o compilador realiza dois deslocamentos realiza dois deslocamentos
para estender o sinal do para estender o sinal do valor de ShortSum para valor de ShortSum para retornar o valor de Test. retornar o valor de Test.
Perda de tempo!Perda de tempo!
Diversos Diversos mov’s mov’s para truncar e para truncar e estender o sinal dos operandos estender o sinal dos operandos
e do resultadoe do resultado
Corrija as suas variáveis shorts
int Test(void)
{
int x,y;
x = 3;
y = 4;
return (short)ShortSum(x, y);
}
int ShortSum(int a, int b)
{
return a+b;
}
;Depois de retornar de ShotSum
mov r1,r0,lsl #16
mov r0,r1,#asr #16
mov pc,lr
ENDP
ShortSum PROC ; start
add r0,r0,r1
mov pc,lr ; return
ENDP
Se necessário manter Se necessário manter o mesmo range de o mesmo range de
short, use castshort, use cast
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;
}
; 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
Já que usamos o Já que usamos o endereço de indx, o endereço de indx, o
compilador é compilador é conservador em mantê-lo conservador em mantê-lo
em um registrador, em um registrador, causando vários acessos causando vários acessos
à memóriaà memória
Aperfeiçoando o Códigoint AliasTaken(int indx)
{
inf ref = indx;
TransIndex(&ref);
indx += 5;
indx += DisplaceIndex(indx);
indx += DitherIndex(indx);
return indx;
}
; ref = indx;
mov r4, r0
; TransIndex(&ref);
add r0, sp, #0
str r4, [sp]
bl TransIndex
; indx += 5;
add r4, r4, #5
; indx += DisplaceIndex(indx);
mov r0, r4
bl DisplaceIndex ; 00000028
add r4, r4, r0
; indx += DitherIndex(indx);
mov r0, r4
bl DitherIndex ; 00000034
add r0, r0, r4
Observe que a variável Observe que a variável indxindx é mantida agora em é mantida agora em r4, sem exigir repetidos r4, sem exigir repetidos STR/LDR para [sp, #0]STR/LDR para [sp, #0]
Módulo VI
Laboratório