# Inline Assembly no GCC

*Inline Assembly* é uma extensão do compilador que permite inserir código Assembly diretamente no código de saída do compilador. Dessa forma é possível misturar C e Assembly sem a necessidade de usar um módulo separado só para o código em Assembly, além de permitir alguns recursos interessantes que não são possíveis sem o *inline* Assembly.

{% hint style="info" %}
O compilador Clang contém uma sintaxe de *inline* Assembly compatível com a do GCC, logo o conteúdo ensinado aqui também é válido para o Clang.
{% endhint %}

## Inline Assembly básico

A sintaxe do uso básico é: `asm [qualificadores] ( instruções-asm )`.

Onde qualificadores é uma (ou mais) das seguintes palavra-chaves:

* **volatile**: Isso desabilita as otimizações de código no *inline* Assembly, mas esse já é o padrão quando se usa o *inline* ASM básico.
* **inline**: Isso é uma "dica" para o compilador considerar que o tamanho do código Assembly é o menor possível. Serve meramente para o compilador decidir se vai ou não expandir uma [função como *inline*](https://mentebinaria.gitbook.io/assembly/funcoes-em-c#inline), e usando esse qualificador você sugere que o código é pequeno o suficiente para isso.

As instruções Assembly ficam dentro dos parênteses como uma *string* literal e são despejadas no código de saída sem qualquer alteração por parte do compilador. Geralmente se usa `\n\t` para separar cada instrução pois isso vai ser refletido literalmente na saída de código. O `\n` é para iniciar uma nova linha e o `\t` (TAB) é para manter a indentação do código de maneira idêntica ao código gerado pelo compilador.

Exemplo:

```c
#include <stdio.h>

int main(void)
{
  asm(
    "mov $5, %eax\n\t"
    "add $3, %eax\n\t"
  );

  return 0;
}
```

Isso produz a seguinte saída ao [visualizar o código de saída](https://mentebinaria.gitbook.io/assembly/programando-junto-com-c/..#vendo-o-codigo-de-saida-do-gcc):

```
main:
	endbr64
	pushq	%rbp
	movq	%rsp, %rbp
#APP
# 5 "main.c" 1
	mov $5, %eax
	add $3, %eax
	
# 0 "" 2
#NO_APP
	movl	$0, %eax
	popq	%rbp
	ret
```

Entre as diretivas `#APP` e `#NO_APP` fica o código despejado do *inline* Assembly. A diretiva `# 5 "main.c" 1` é apenas um atalho para a diretiva `#line` onde ela serve para avisar para o assembler de qual linha (5) e arquivo ("main.c") veio aquele código. Assim se ocorrer algum erro, na mensagem de erro do assembler será exibido essas informações.

Repare que o *inline* Assembly apenas despeja literalmente o conteúdo da *string* literal. Logo você pode adicionar o que quiser aí incluindo diretivas, comentários ou até mesmo instruções inválidas que o compilador não irá reclamar.

Também é possível usar *inline* Assembly básico fora de uma função, como em:

```c
#include <stdio.h>

int add(int, int);

int main(void)
{
  printf("%d\n", add(2, 3));
  return 0;
}

asm (
  "add:\n\t"
  "  lea (%edi, %esi), %eax\n\t"
  "  ret"
);
```

Porém não é possível fazer o mesmo com *inline* Assembly estendido.

## Inline Assembly estendido

A versão estendida do *inline* Assembly funciona de maneira semelhante ao *inline* Assembly básico, porém com a diferença de que é possível acessar variáveis em C e fazer saltos para rótulos no código fonte em C.

A sintaxe da versão estendida segue o seguinte formato:

```c
asm [qualificadores] (
  "instruções-asm"
  : operandos-de-saída
  : operandos-de-entrada
  : clobbers
  : rótulos-goto
)
```

Os qualificadores são os mesmos da versão básica porém com mais um chamado **goto**. O qualificador **goto** indica que o código Assembly pode efetuar um salto para um dos rótulos listados no último operando. Esse qualificador é necessário para se usar os rótulos no código ASM. Enquanto o qualificador **volatile** desabilita a otimização de código, que é habilitada por padrão no *inline* Assembly estendido.

Dentre esses operandos somente os de saída são "obrigatórios", os demais podem ser omitidos. E todos eles podem conter uma lista vazia exceto o de rótulos.

Existe um limite **máximo de 30 operandos** com a soma dos operandos de saída, entrada e rótulos.

### operandos-de-saída

Cada operando de saída é separado por vírgula e contém a seguinte sintaxe:

```c
[nome] "restrições" (variável)
```

Onde `nome` é um símbolo opcional que você pode criar para se referir ao operando no código Assembly. Também é possível se referir ao operando usando `%n`, onde **n** seria o índice do operando (contando a partir de zero). E usar `%[nome]` caso defina um nome.

Como o `%` é usado para se referir à operandos, no *inline* Assembly estendido se usa dois `%` para se referir à um registrador. Já que `%%` é um escape para escrever o próprio `%` na saída, da mesma forma que se faz na função **printf**.

[As restrições](#restricoes) é uma *string* literal contendo letras e símbolos indicando como esse operando deve ser armazenado (**r** para registrador e **m** para memória, por exemplo). No caso dos operandos de saída o primeiro caractere na *string* deve ser um `=` ou `+`. Onde o `=` indica que a variável terá seu valor modificado, enquanto `+` indica que terá seu valor modificado e lido.

Operandos de saída com `+` são contabilizados como dois, tendo em vista que o `+` é basicamente um atalho para repetir o mesmo operando também como uma entrada.

Essas informações são necessárias para que o compilador consiga otimizar o código corretamente. Por exemplo caso você indique que a variável será somente escrita com `=` mas leia o valor da variável no Assembly, o compilador pode assumir que o valor da variável nunca foi lido e portanto descartar a inicialização dela durante a otimização de código. Isso criaria um comportamento estranho no *inline* Assembly onde se obteria lixo como valor da variável.

Um exemplo deste **erro**:

```c
#include <stdio.h>

int main(void)
{
  int x = 5;

  asm("addl $3, %0"
      : "=rm"(x));

  printf("%d\n", x);
  return 0;
}
```

A otimização de código pode remover a inicialização `x = 5` já que não informamos que o valor dessa variável é lido dentro no *inline* Assembly. O correto seria usar `+` nesse caso.

Um exemplo (dessa vez correto) usando um nome definido para o operando:

```c
#include <stdio.h>

int main(void)
{
  int x;

  asm("movl $5, %[myvar]"
      : [myvar] "=rm"(x));

  printf("%d\n", x);
  return 0;
}
```

{% hint style="info" %}
Caso utilize um operando que você não tem **certeza** que será armazenado em um registrador, lembre-se de usar [o sufixo na instrução](https://mentebinaria.gitbook.io/assembly/sintaxe-do-gas#tamanho-dos-operandos) para especificar o tamanho do operando. Para evitar erros é ideal que sempre use os sufixos.
{% endhint %}

### operandos-de-entrada

Os operandos de entrada seguem a mesma sintaxe dos operandos de saída porém sem o `=` ou `+` nas restrições. Não se deve tentar modificar operandos de entrada (embora tecnicamente seja possível) para evitar erros, lembre-se que o compilador irá otimizar o código assumindo que aquele operando não será modificado.

Também é possível passar expressões literais como operando de entrada ao invés de somente nomes de variáveis. A expressão será avaliada e seu valor passado como operando sendo armazenado de acordo com as restrições.

### clobbers

*Clobbers* (que eu não sei como traduzir) é basicamente uma lista, separada por vírgula, de efeitos colaterais do código Assembly. Nele você **deve** listar o que o seu código ASM modifica além dos operandos de saída. Cada valor de *clobber* é uma *string* literal contendo o nome de um registrador que é modificado pelo seu código. Também há dois nomes especiais de *clobbers*:

| Clobber | Descrição                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cc      | Indica que o código ASM modifica as *flags* do processador (registrador [EFLAGS](https://mentebinaria.gitbook.io/assembly/aprofundando-em-assembly/flags-do-processador)).                                                                                                                                                                                                                                                                                                                         |
| memory  | <p>Indica que o código ASM faz leitura ou escrita da/na memória em outro lugar que não seja um dos operandos de entrada ou saída. Por exemplo em uma memória apontada por um ponteiro de um operando.</p><p></p><p>Esse <em>clobber</em> evita que o compilador assuma que os valores das variáveis na memória permanecem os mesmos após a execução do código ASM. E também garante que o compilador escreva o valor de todas as variáveis na memória antes de executar o <em>inline</em> ASM.</p> |
| rax     | Indica que o registrador RAX será modificado.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| rbx     | Indica que o registrador RBX será modificado.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| etc.    | ...                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

Qualquer nome de registrador é válido para ser usado como *clobber* exceto o *Stack Pointer* (RSP). É esperado que no final da execução do *inline* ASM o valor de RSP seja o mesmo de antes da execução do código. Se não for o código muito provavelmente irá ter problemas no restante da execução.

Quando você adiciona um registrador a lista de *clobbers* ele não será utilizado para armazenar operandos de entrada ou saída, assim garantindo que o registrador pode ser utilizado livremente no *inline* ASM sem causar qualquer erro. Isso também garante que o compilador não irá assumir que o valor do registrador permanece o mesmo após a execução do *inline* ASM.

Exemplo:

```c
int add(int a, int b)
{
  int result;

  asm("movl %[a], %%eax\n\t"
      "addl %[b], %%eax\n\t"
      "movl %%eax, %[result]"
      : [result] "=rm"(result)
      : [a] "r"(a),
        [b] "r"(b)
      : "cc",
        "eax");

  return result;
}
```

### rótulos-goto

Ao usar `asm goto` pode-se referir à um rótulo usando o prefixo `%l` seguido do índice do operando de rótulo. Onde a contagem inicia em zero e é contabilizado também os operandos de entrada e saída.

Exemplo:

```c
#include <stdio.h>
#include <stdbool.h>

int do_anything(bool value)
{
  int result = 3;

  asm goto("test %[value], %[value]\n\t"
           "jz %l1"
           :
           : [value] "r"(value)
           : "cc"
           : my_label);

  result += 2;

my_label:
  return result;
}

int main(void)
{
  printf("%d, %d\n", do_anything(true), do_anything(false));
  return 0;
}
```

Mas felizmente também é possível usar o nome do rótulo no *inline* Assembly, bastando usar a notação `%l[nome]`. O exemplo acima poderia ter a instrução de salto reescrita para `jz %l[my_label]`.

## Restrições

As restrições (*constraints*) são uma lista de caracteres que determinam onde um operando deve ser armazenado. É possível indicar múltiplas alternativas para o compilador simplesmente adicionando mais de uma letra indicando tipos de armazenamento diferentes.

Abaixo a lista de algumas restrições disponíveis no GCC.

### Restrições comuns

| Restrição | Descrição                                                                                                                          |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `m`       | Operando na memória.                                                                                                               |
| `r`       | Operando em um [registrador de propósito geral](https://mentebinaria.gitbook.io/assembly/a-base/registradores-de-proposito-geral). |
| `i`       | Um valor inteiro imediato.                                                                                                         |
| `F`       | Um valor *floating-point* imediato.                                                                                                |
| `g`       | Um operando na memória, registrador de propósito geral ou inteiro imediato. Mesmo efeito que usar `"rim"` como restrição.          |
| `p`       | Um operando que é um endereço de memória válido.                                                                                   |
| `X`       | Qualquer operando é permitido. Basicamente deixa a decisão nas mãos do compilador.                                                 |

### Restrições para família x86

| Restrição | Descrição                                                                                                                                |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `R`       | Registradores legado. Qualquer um dos oito registradores de propósito geral disponíveis em IA-32.                                        |
| `q`       | Qualquer registrador que seja possível ler o byte menos significativo. Como RAX (AL) ou R8 (R8B) por exemplo.                            |
| `Q`       | Qualquer registrador que seja possível ler o segundo byte menos significativo, como RAX (AH) por exemplo.                                |
| `a`       | O registrador "A" (RAX, EAX, AX ou AL).                                                                                                  |
| `b`       | O registrador "B" (RBX, EBX, BX ou BL).                                                                                                  |
| `c`       | O registrador "C" (RCX, ECX, CX ou CL).                                                                                                  |
| `d`       | O registrador "D" (RDX, EDX, DX ou DL).                                                                                                  |
| `S`       | RSI, ESI, SI ou SIL.                                                                                                                     |
| `D`       | RDI, EDI, DI ou DIL.                                                                                                                     |
| `A`       | O conjunto AX:DX.                                                                                                                        |
| `f`       | Qualquer [registrador do x87](https://mentebinaria.gitbook.io/assembly/aprofundando-em-assembly/usando-instrucoes-da-fpu#registradores). |
| `t`       | ST0                                                                                                                                      |
| `u`       | ST1                                                                                                                                      |
| `y`       | Qualquer registrador MMX.                                                                                                                |
| `x`       | Qualquer [registrador SSE](https://mentebinaria.gitbook.io/assembly/aprofundando-em-assembly/entendendo-sse#registradores-xmm).          |
| `Yz`      | XMM0                                                                                                                                     |
| `I`       | Um inteiro constante entre 0 e 31, usado para *shift* com valores de 32-bit.                                                             |
| `J`       | Um inteiro constante entre 0 e 63, usado para *shift* com valores de 64-bit.                                                             |
| `K`       | Inteiro sinalizado de 8-bit.                                                                                                             |
| `N`       | Inteiro não-sinalizado de 8-bit.                                                                                                         |

## Dicas

### Rótulos locais no inline Assembly

Se você simplesmente declarar rótulos dentro do *inline* Assembly pode acabar se deparando com uma redeclaração de símbolo por não ter uma garantia de que ele seja único. Mas uma dica é usar o escape especial `%=` que expande para um número único para cada uso de `asm`, assim sendo possível dar um nome único para os rótulos.

Exemplo:

```c
#include <stdio.h>
#include <stdbool.h>

int do_anything(bool value)
{
  int result = 3;

  asm("test %[value], %[value]\n\t"
      "jz .my_label%=\n\t"
      "addl $2, %[result]\n\t"
      ".my_label%=:"
      : [result] "+a"(result)
      : [value] "r"(value)
      : "cc");

  return result;
}

int main(void)
{
  printf("%d, %d\n", do_anything(true), do_anything(false));
  return 0;
}
```

### Usando sintaxe Intel

Caso prefira usar sintaxe Intel é possível fazer isso meramente compilando o código com `-masm=intel`. Isso porque o *inline* Assembly simplesmente despeja as instruções no arquivo de saída, portanto o código irá usar a sintaxe que o *assembler* utilizar.

Outra dica é usar a diretiva `.intel_syntax noprefix` no início, e depois `.att_syntax` no final para religar a sintaxe AT\&T para o restante do código. Exemplo:

```c
int add(int a, int b)
{
  int result;

  asm(".intel_syntax noprefix\n\t"
      "lea %[result], [ %[a] + %[b] ]\n\t"
      ".att_syntax"
      : [result] "=a"(result)
      : [a] "r"(a),
        [b] "r"(b));

  return result;
}
```

### Escolhendo o registrador/símbolo para uma variável

Ao usar o *storage-class* `register` é possível escolher em qual registrador a variável será armazenada usando a seguinte sintaxe:

```c
register int x asm("r12") = 5;
```

Nesse exemplo a variável `x` **obrigatoriamente** seria alocada no registrador R12.

Também é possível escolher o nome do símbolo para variáveis locais com *storage-class* `static` ou para variáveis globais. Como em:

```c
static int x asm("my_var") = 5;
```

A variável no código fonte é referida como `x` mas o símbolo gerado para a variável seria definido como `my_var`.
