Uma breve introdução ao futuro: Web Assembly

O que é Web Assembly?
Web Assembly é uma das novas tecnologias mais importantes que chegaram ao ambiente web em talvez uma década, possivelmente desde os JITs. Você pode ler mais sobre JITs (compiladores just in time) neste artigo aqui. Assim como os JITs, Web Assembly promete melhorias importantes nos aplicativos web.
Em poucas palavras, Web Assembly permite executar no navegador código diferente do seu código javascript. Ou, nas palavras da Mozilla: "um padrão emergente cujo objetivo é definir um destino binário para compiladores seguro, portátil e eficiente em tamanho e tempo de carregamento, oferecendo desempenho quase nativo: uma CPU virtual para a web."
Certo, Web Assembly permite executar meu código (rust/c/c++) no navegador. É interessante, mas como funciona? Para responder, precisamos conhecer um pouco mais sobre código de máquina, projeto de compiladores e tudo relacionado. Vamos aprofundar.
Como funciona Web Assembly?
Quando um programa escrito em uma linguagem de alto nível é compilado, passa por uma série de transformações até chegar ao código assembly que será interpretado pela máquina.
Como cada arquitetura suporta seu próprio dialeto assembly, para cada linguagem de alto nível precisaríamos criar uma transformação específica para cada dialeto. Se temos n linguagens e m dialetos, precisaríamos de n \* m formas de transformação.
Como isso não é ideal, o compilador tem uma representação intermediária do código-fonte. Qualquer que seja a linguagem de alto nível utilizada, a IR será a mesma para todas. Isso evita trabalhar com as outras n \* m transformações e deixa apenas uma. O compilador tem um frontend que transforma o código-fonte em IR e um backend que a transforma em um dialeto específico compreendido pela máquina.

Poderíamos dizer que Web Assembly é um desses dialetos, mas não seria totalmente preciso, pois cada dialeto é único e determinado pela arquitetura de uma máquina física.
Quando enviamos código à máquina de um usuário pela internet, não sabemos nada sobre seu computador ou arquitetura. Por isso, Web Assembly é diferente dos outros dialetos assembly: é um dialeto virtual que será executado em uma CPU virtual.
O que diferencia Web Assembly de outros dialetos assembly é ser um dialeto virtual executado em uma CPU virtual.
O navegador baixa esse dialeto Web Assembly, e o código tem um atalho para o dialeto específico da máquina, sem tempo de compilação, ao contrário do código javascript e dos JITs.

Atualmente, WebAssembly está na fase de produto mínimo viável, ou MVP (você pode ler mais sobre ela aqui. É suportado pelos quatro navegadores principais (Chrome, Firefox, Safari e Opera) e busca substituir asm.js, usado, ou que já foi usado, para levar código de linguagens como c/c++ para a web.
O compilador com maior suporte a WebAssembly é LLVM, e muitos frontends e backends podem ser conectados a ele. Também é uma cadeia de ferramentas de compilação amplamente testada.
Configurar LLVM leva tempo, mas você encontra as instruções aqui.
No próximo exemplo, usaremos WebAssebly fiddle, que você pode testar aqui. Ele permite baixar os módulos WebAssembly produzidos pelos programas em c que escrevermos.
Atualmente, WebAssembly não consegue se comunicar com nenhuma API web, e o navegador não consegue baixar módulos web assembly. Isso será implementado no futuro; enquanto isso, usaremos javascript para carregar os módulos.
Fibonacci
Para começar, vamos fazer fibonacci em C. Precisaremos de python para criar um servidor simples e de WebAssembly fiddle.
Começamos criando o arquivo fib.c, uma implementação simples e recursiva de fibonacci.
int fib (int number) {
if (number <= 1) return 1;
return fib(number - 1) + fib(number - 2);
}
Coloque este código no Web Assembly fiddle e pressione o botão Build. Você obterá a representação textual do módulo Web Assembly com sintaxe de expressões S.

Por enquanto, não vamos aprofundar essa representação nem o formato, mas você pode consultar aqui se quiser.
Baixe o arquivo Web Assembly, não o arquivo wast 😅, e coloque ao lado de fib.c.
Depois, vamos criar o javascript que atua como código de ligação para carregar nosso programa em C.
Usaremos a API Web Assembly para javascript para compilar e instanciar o módulo.
Você pode consultar essa API aqui.
Vamos implementar uma função auxiliar em javascript para obter o arquivo WebAssembly e instanciá-lo:
(async (global) => {
async function fetchAndInstantiateWasm (url) {
let binary = await fetch(url)
let bytes = await binary.arrayBuffer()
let module = await WebAssembly.compile(bytes)
let instance = await WebAssembly.instantiate(module)
return instance
}
let module = await fetchAndInstantiateWasm("./fib.wasm")
global.fib = module.exports.fib;
})(window);
Na primeira linha de fetchAndInstantiateWasm, obtemos o arquivo WebAssembly, que é a representação binária do módulo. Na segunda, guardamos esse binário em um array buffer, um array com números binários como [1111010, 10001111, 10000101, 00001111, …, 10001111]. Nas linhas seguintes, retornamos o módulo compilado e instanciado.
Depois chamamos fetchAndInstantiateWasm, guardamos o resultado na variável module e colocamos no escopo global.
Uma instância de module sempre contém suas funções compiladas, no nosso caso a partir de C, na chave exports. Nosso módulo terá esta forma:
{
memory: {…}
exports: {
fib: function () { [native code]}
}
}
A chave memory contém informações sobre a memória do WebAssembly durante a execução, compartilhada com javascript.
Agora precisamos levar isso ao navegador. Criamos fib.html e carregamos o módulo js que carregará web assembly.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript" src="./fib.js"></script>
</body>
</html>
Salvamos o arquivo junto aos outros. A estrutura do nosso pequeno projeto deve se parecer com isto:

Por fim, servimos esses arquivos com python: execute python -m SimpleHTTPServer 8080 ou, se tiver python 3, python -m http.server 8080.
Abra o navegador e acesse localhost:8080, entre em fib.html, abra o console e... pronto! Você deve ter uma função nativa fib global.

Um bom repositório para continuar lendo sobre Web Assembly é este, que tem muitas informações bastante atualizadas.