Como aprender LLVM vai te ajudar a entender como computadores funcionam.


Introdução

Eu sou uma pessoa que gosta muito de mecher com linguagens de programação e inclusive criar linguagens. Recentemente eu me dei de cara com uma tecnologia que já conhecia mas tinha receio de usar, o LLVM.

Para quem não sabe, o LLVM é um kit de desenvolvimento de compiladores. Várias linguagens (C [clang], Rust, Go, etc.) utilizam o LLVM para compilarem o código para um executável de várias plataformas sem terem que escrever Assembly à mão.

Mas como?

A principal forma do LLVM conseguir compilar código para (praticamente) qualquer sistema é utilizando uma linguagem intermediária que eles chamam de LLVM IR (ou só IR).

Essa linguagem é como se fosse um C misturado com x86-64 Assembly extremamente tipada mas bastante simples de entender. ificado anteriormente).

Basicamente, o LLVM (a biblioteca) é um wrapper em volta desse IR para que a engine (motor) do LLVM processe essa linguagem e compile para o sistema operacional que está sendo utilizado (ou que foi especificado)

LLVM IR na prática!

Vou mostrar um simples hello world feito de uma das formas mais amigáveis possíveis em LLVM IR. E depois, vamos desmontar esse código e entender como a linguagem funciona.

Analise o código:

target triple = "x86_64-unknown-linux-gnu"
target datalayout = ""

@"string" = internal constant [15 x i8] c"Hello, World!\0a\00"

define i8 @"main"()
{
entry:
  %mensagem = bitcast [15 x i8]* @"string" to i8*
  %"chamada printf" = call i32 (i8*, ...) @"printf"(i8* %mensagem)
  ret i8 0
}

Sim! Eu sei que pode parecer um bicho de sete cabeças. Mas quando você entende o por quê dessas coisas estarem aí, não parecerá mais.

Bom, primeira coisa que devemos considerar são as duas primeiras linhas do código:

  • target triple = "x86_64-unknown-linux-gnu": Essa linha define que o target (alvo) de compilação é Linux x86-64 para poder ser compilado depois.
  • target datalayout = "": Define de que forma o compilador vai guardar dados na memória (nesse caso está em branco pois não quero mudar nada)
  • @"string" = internal constant [15 x i8] c"Hello, World!\0a\00": Agora temos uma definição de uma constante que é um array de i8 (char nesse caso) com a nossa mensagem que chamei de string
  • define i8 @"main"(): Agora definimos a função do ponto de entrada do programa main
  • entry:: Isso define o label (etiqueta) principal que o programa vai iniciar.
  • %mensagem = bitcast [15 x i8]* @"string" to i8*: Essa linha converte o endereço da constante string para um ponteiro de tipo i8* (ponteiro para i8, que é o tipo de dado para caracteres no LLVM IR). Essa conversão é feita usando a instrução bitcast.
  • %"chamada printf" = call i32 (i8*, ...) @"printf"(i8* %mensagem): Aqui temos uma chamada de função para printf, uma função externa definida em C que imprime uma string formatada na saída padrão. Passamos o ponteiro mensagem como argumento para printf.
  • ret i8 0: Essa instrução retorna o valor 0 (que indica uma execução bem-sucedida) do ponto de entrada main.

Esse código é um exemplo simples de um programa "Hello, World!" escrito em LLVM IR. Ele declara uma constante string contendo a mensagem a ser impressa e, em seguida, chama a função printf para imprimir essa mensagem. O programa retorna 0 para indicar que foi executado com sucesso.

Explorar o LLVM e sua linguagem intermediária pode ser uma experiência interessante para quem gosta de programação e desenvolvimento de linguagens. O LLVM oferece uma série de ferramentas e bibliotecas que permitem criar compiladores eficientes e portáveis.

Se você tiver interesse em se aprofundar no assunto, recomendo consultar a documentação oficial do LLVM, que possui informações detalhadas sobre o funcionamento do kit de desenvolvimento, suas ferramentas e sua linguagem intermediária.

Aprender sobre o LLVM IR é bom para aprofundar seus conhecimentos sobre o funcionamento das linguagens de baixo nível e ainda por cima aprende uma tecnologia muito interessante e útil.

Bons Estudos!

É uma boa postagem e acho bom que outras pessoas tenham algum contato com esse assunto.

É verdade que seria benéfico para entender melhor sobre a computação, mas o LLVM é um pouco complexo. Pode ser uma boa para a pessoa ver mais pra frente. O bo me velho Assembly é mais fácil ainda. E provavelmente dá um ideia até melhor junto com um compilador C. É uma questão de curiosidade e saber como construir esse aprendizado.

Espero ter ajudado.


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).

Sim, concordo que para iniciantes pode ser muito complexo. Mas o meu objetivo nesse post é fazer com que gente mais experiente aprenda LLVM. Inclusive vi que tu apareceu num video do Lucas Montano do youtube. Tu parece ser um cara mt dahora. Se tu quiser tmb pode dar uma olhada no meu Github. Faço umas coisinhas legais [lá](https://github.com/marc-dantas).

Eu tenho uma dúvida pra você que deve saber... LLVM é programado em C++ , e Rust é feito de código do LLVM, logo é programado em algo que é c++, como o Rust é quase da mesma velocidade do C++, ora ele não é feito de C++?, é como se você ligasse um gerador de energia na tomada

Veja no [GitHub](https://github.com/rust-lang/rust) que o compilador de Rust é escrito na própria linguagem (mais de 96% do código é Rust, e nada de C++). Este compilador gera o código intermediário do LLVM, que por sua vez gera o executável - o processo todo é descrito [aqui](https://rustc-dev-guide.rust-lang.org/overview.html). Para entender como o compilador de uma linguagem pode ser escrito nela mesma, leia [aqui](https://pt.stackoverflow.com/a/178611/112052) (o link é sobre Java, mas a ideia geral é a mesma para qualquer linguagem). De qualquer forma, não é a linguagem em que o compilador foi escrito que determina a "velocidade", e sim o que/como a linguagem faz as coisas. Por exemplo, [a implementação atual do Python é escrita em C](https://github.com/python/cpython), mas muitas coisas em Python não chegam na mesma velocidade do C porque a linguagem tem um *overhead* interno - como o fato de cada valor (seja número, string, etc) ser um objeto complexo: uma simples soma de dois números resulta na chamada de um método. O mesmo vale para qualquer linguagem.
Só para complementar, Rust, de acordo com a implementação oficial, gera um código intermediário que é então traduzido para o código intermediário do LLVM, exemplificado na postagem original. Não é tão simples explicar a questão da velocidade sem que a pessoa tenha um entendimento amplo da computação e principalmente, deixar de lado os mitos que aprenderam vendo coisas na internet (sempre fala que ela é a casa do capeta, ela útil, mas ótima para ensinar algo errado, assim como o papel, ela aceita tudo, até muita coisa errada, e que pode prevalecer). Por isso que eu falo sempre para as pessoas não treinarem o erro. A comparação com o gerador de energia não parece fazer sentido, ou eu não entendi o propósito. De qualquer forma o mais importante é que as linguagens não possuem velocidade. As linguagens não são o que as pessos pensam que são. Não vou entrar em todos os detalhes, correndo o risco de um entendimento errado pela falta de detalhes, mas não é o foco aqui. Rust, dentro da normalidade, roda código binário igual C++. Não o mesmo código binário, até porque o código fonte nunca é o mesmo, e as linguagens possuem regras muito diferentes, mesmo um cósigo simples, pouco mais que um Hello World, que é semanticamente idêntico nas duas linguagens gerarão binários bem diferentes. Roda igual C, Delphi, Go, C#, em Java em última análise, e várias outras linguagens. Tem Python que faz isso, mas "ninguém" usa. Links úteis: - https://pt.stackoverflow.com/q/104814/101 - https://pt.stackoverflow.com/q/454543/101 - https://pt.stackoverflow.com/q/101691/101
Lua é considerada uma linguagem de alto desempenho, e ela é interpretada! Se usar o LuaJit, ela tem partes do codigo compilado o que a torna ainda mais rapida! Não é no quê foi escrita, mas sim como foi escrita. Rust é quase da mesma velocidade do C++ mas ela não é C++! Uma vez escrita e compilada, ela deixa de ser C/C++ e passa a ser Rust ou compilador Rust, Logo, com esse Compildor Rust você pode escrever Rust e compilar um novo Rust! O fato está em como ela foi projetada para funcionar.
Exatamente. Além do Lua, temos o PyPy que também usa JIT e também a nova linguagem de programação Mojo que também tem JIT no meio!