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 destring
define i8 @"main"()
: Agora definimos a função do ponto de entrada do programamain
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!