06 - Condicionais e funções
Na aula de hoje vamos revisar e praticar os conceitos de funções e condicionais em Assembly. Teremos exercícios simples de cada assunto e no fim alguns exercícios para entrega que juntam coisas diferentes.
Funções e aritmética com LEA
Todos os exercícios da revisão serão feitos com o arquivo exemplo1 (compilado a partir de exemplo1.c). Vamos examinar tanto a função main quanto a função exemplo1.
Compile com:
Chamadas de funções
As chamadas de função são feitas usando a seguinte ordem para os argumentos inteiros:
%rdi%rsi%rdx%rcx%r8%r9
Esta ordem nunca muda. Veja abaixo um exemplo de chamada de função tirado do main de exemplo1.
0x01163 <+8>: mov $0x6,%r9d
0x01169 <+14>: mov $0x5,%r8d
0x0116f <+20>: mov $0x4,%ecx
0x01174 <+25>: mov $0x3,%edx
0x01179 <+30>: mov $0x2,%esi
0x0117e <+35>: mov $0x1,%edi
0x01183 <+40>: call 0x1149 <exemplo1>
0x01188 <+45>: lea 0xa(%rax),%esi
Pergunta 1
Answer
A ordem dos parâmetros segue é sempre a mesma vista na aula 04. Mesmo que as instruções estejam em ordem diferente, %edi (ou uma de suas partes) é sempre o primeiro parâmetro.
Exercise 2
Answer
exemplo1(1, 2, 3, 4, 5, 6)
Vamos agora analisar o código de exemplo1:
Dump of assembler code for function exemplo1:
0x01149 <+0>: endbr64
0x0114d <+4>: add %esi,%edi
0x0114f <+6>: add %edx,%edi
0x01151 <+8>: add %ecx,%edi
0x01153 <+10>: add %r8d,%edi
0x01156 <+13>: lea (%rdi,%r9,1),%eax
0x0115a <+17>: ret
Exercise 3
Answer
Pelos registradores utilizados, percebemos que a função tem seis parâmetros, todos int.
O retorno também é int.
Exercise 4
Answer
int exemplo1(int a, int b, int c, int d, int e, int f);
Exercise 5
Answer
Ela soma os primeiros 5 argumentos: a + b + c + e.
Vemos na linha exemplo1+13 que colocamos um valor no registrador %eax e depois finalizamos a função usando ret. Este é o segundo ponto que nunca muda: o valor de retorno de toda função é colocado no registrador %rax (ou uma de suas partes menores). Neste exemplo, a instrução usada foi o LEA que relembraremos na seção a seguir.
Operações aritméticas usando LEA
Se usada de maneira literal, a instrução LEA (Load Effective Address) serve para calcular o endereço de uma variável local e é equivalente ao operador & em C. Porém, ela é frequentemente "abusada" para fazer aritmética. Um ponto importante quando usamos LEA é que todos os operandos são registradores de 64 bits.
Regra geral 1
- Se
LEAfor usada com o registrador%rspentão ela sempre representa o operador& - Se os registradores envolvidos foram usados como números inteiros em instruções anteriores, então ela representa uma conta com os valores dos registradores.
Vejamos o exemplo da função exemplo1 acima:
No exemplo acima LEA é usada para fazer aritmética. Sabemos disso pois, na chamada traduzida na parte anterior, elas recebem números inteiros (%r9d = 6 e %edi = 1 mais o resultado de sucessivas somas). Seu primeiro argumento segue a seguinte lógica
Cé uma constante%R1é um registrador%R2é um registrador (pode ser igual a%R1)Sé1, 2, 4ou8(todos os tamanhos possíveis de registradores inteiros)
A operação acima calcula C + %R1 + (%R2 * S). A operação LEA nunca acessa a memória, apenas move o resultado deste cálculo para o registrador destino. Qualquer outra operação que use a sintaxe acima está fazendo um acesso a memória. LEA é a única exceção!
Exercise 6
Answer
Supondo que aux = a+b+c+d+e já foi calculado com o uso das instruções ADD, então o LEA calcula aux + f*1.
Exercise 7
Answer
Confira no arquivo exemplo1.c
Retorno de funções
Vamos terminar nossa revisão analisando novamente a chamada de exemplo1 no main:
0x01163 <+8>: mov $0x6,%r9d
0x01169 <+14>: mov $0x5,%r8d
0x0116f <+20>: mov $0x4,%ecx
0x01174 <+25>: mov $0x3,%edx
0x01179 <+30>: mov $0x2,%esi
0x0117e <+35>: mov $0x1,%edi
0x01183 <+40>: call 0x1149 <exemplo1>
0x01188 <+45>: lea 0xa(%rax),%esi
Anteriormente já vimos que o call e os mov s acima fazem a chamada exemplo1(1,2,3,4,5,6) em C.
A linha de baixo realiza uma operação aritmética com %rax.
Exercise 8
Answer
int esi = exemplo1(1, 2, 3, 4, 5, 6) + 10;
Exercícios combinados
Warning
Todos os exercícios desta seção são para entrega. Vocês podem se conversar para fazê-los, mas cada um deve criar sua própria solução do zero. Todos os exercícios já estão disponíveis no seu repositório de entregas da disciplina em atv/04-condicionais-funcoes. Leia o README dentro da pasta para mais informações.
Função ex1: Aritmética e Expressões booleanas.
Dump of assembler code for function ex1:
0x000 <+0>: endbr64
0x004 <+4>: lea (%rdi,%rsi,1),%rax
0x008 <+8>: lea (%rax,%rdx,4),%rcx
0x00c <+12>: imul %rdi,%rdi
0x010 <+16>: lea (%rdi,%rsi,2),%rax
0x014 <+20>: add %rax,%rdx
0x017 <+23>: cmp %rdx,%rcx
0x01a <+26>: setge %al
0x01d <+29>: movzbl %al,%eax
0x020 <+32>: ret
Exercise 9
Exercise 10
Exercise 11
Exercise 12
Exercise 13
Example
Usando as perguntas acima preencha o arquivo de solução no repositório e execute os testes.
Função ex2: Chamadas de funções e Condicionais.
Quando analisar o código do ex2 no gdb, utilize o arquivo ex2_ref para visualizar corretamente as chamadas de funções. Seria uma boa também consultar as relocation entries no terminal com objdump -r ex2.o.
Dump of assembler code for function ex2:
0x01132 <+0>: endbr64
0x01136 <+4>: push %rbx
0x01137 <+5>: mov %rdi,%rbx
0x0113a <+8>: mov %rsi,%rdi
0x0113d <+11>: call 0x1129 <vezes2>
0x01142 <+16>: cmp %rbx,%rax
0x01145 <+19>: jle 0x114a <ex2+24>
0x01147 <+21>: add %rbx,%rbx
0x0114a <+24>: add %rbx,%rax
0x0114d <+27>: pop %rbx
0x0114e <+28>: ret
Exercise 14
Vamos começar trabalhando na linha ex2+11, na instrução call vezes2 . A chamada necessita usar o registrador %rdi, mas ele contém o primeiro argumento de ex2.
Exercise 15
Exercise 16
Exercise 17
Você deve ter notado as instruções push/pop %rbx no começo/fim da função. Toda função pode usar os registradores de argumentos (vistos na parte 1) e o de valor de retorno como quiserem. Se precisarem mexer nos outros registradores a prática é salvá-los na pilha no começo da função e restaurá-los no fim. Assim não importa o que a função faça, para a função chamadora é como se não houvesse havido nenhuma modificação nos outros registradores.
Vamos agora olhar a condicional na linha ex2+16.
Exercise 18
Exercise 19
Exercise 20
Example
Usando as perguntas acima preencha o arquivo de solução no repositório e execute os testes.
Função ex3: Ponteiros e Expressões booleanas.
Dump of assembler code for function ex3:
0x000 <+0>: endbr64
0x004 <+4>: cmp %rsi,%rdi
0x007 <+7>: setl %al
0x00a <+10>: movzbl %al,%eax
0x00d <+13>: mov %eax,(%rdx)
0x00f <+15>: sete %al
0x012 <+18>: movzbl %al,%eax
0x015 <+21>: mov %eax,(%rcx)
0x017 <+23>: setg %al
0x01a <+26>: movzbl %al,%eax
0x01d <+29>: mov %eax,(%r8)
0x020 <+32>: ret
Exercise 21
Exercise 22
Exercise 23
Exercise 24
Example
Usando as perguntas acima preencha o arquivo de solução no repositório e execute os testes.
Função ex4: Ponteiros e Expressões booleanas.
Dump of assembler code for function ex4:
0x00a <+0>: endbr64
0x00e <+4>: cmp $0x11,%di
0x012 <+8>: jle 0x21 <ex4+23>
0x014 <+10>: sub $0x41,%esi
0x017 <+13>: cmp $0x1,%sil
0x01b <+17>: ja 0x29 <ex4+31>
0x01d <+19>: lea -0x11(%rdi),%eax
0x020 <+22>: ret
0x021 <+23>: mov $0x12,%eax
0x026 <+28>: sub %edi,%eax
0x028 <+30>: ret
0x029 <+31>: mov $0xffffffff,%eax
0x02e <+36>: ret
Exercise 25
Exercise 26
Exercise 27
Exercise 28
Example
Usando as perguntas acima preencha o arquivo de solução no repositório e execute os testes.