Shopping cart

Subtotal $0.00

View cartCheckout

Building better devs

TnewsTnews
Programação

46 Milhoes de Linhas: O Compilador Rust Traduzido para C

Email : 6

46 Milhões de Linhas: O Compilador Rust Traduzido para C

Imagine pegar um compilador inteiro, com todas as suas otimizações, análises de lifetime, borrow checker e type system, e converter tudo isso para outra linguagem. Não estamos falando de um projeto de brinquedo. Estamos falando do rustc, o compilador oficial do Rust, na versão nightly 1.98.0, traduzido linha por linha para 46 milhões de linhas de código C.

O projeto se chama crustc, e ele faz exatamente o que parece impossível: gera um compilador Rust funcional que você compila com GCC e make. Sem LLVM. Sem toolchain especial. Só C puro.

O problema que ninguém queria resolver

Rust tem um problema que a comunidade prefere não discutir muito: a dependência brutal do LLVM. Se você quer compilar Rust para uma plataforma, precisa que o LLVM suporte essa plataforma. E o LLVM, apesar de gigante, não cobre tudo.

Existe hardware legado rodando em produção há décadas. Mainframes, sistemas embarcados industriais, arquiteturas obscuras que só têm um compilador C velho e mal-humorado. Para essas plataformas, Rust simplesmente não existe. Você pode amar o borrow checker o quanto quiser, mas se o LLVM não gera código para o seu chip, acabou a conversa.

O FractalFir, desenvolvedor por trás do crustc, decidiu atacar esse problema pela raiz. A ideia: se praticamente toda plataforma do mundo tem um compilador C, por que não traduzir o Rust para C e deixar o compilador local fazer o trabalho pesado?

Parece simples. Levou 14 tentativas e 3 anos.

Cilly: o backend que faz a mágica

O coração do crustc é uma ferramenta chamada cilly (sim, o nome é uma piada com “silly”). Cilly funciona como um backend alternativo para o compilador Rust. Em vez de gerar código de máquina via LLVM, ele gera código C.

Mas não é uma tradução ingênua. O maior desafio de traduzir Rust para C é que as duas linguagens têm modelos de memória fundamentalmente diferentes. Rust garante segurança de memória em tempo de compilação. C não garante nada, e ainda tem undefined behavior espalhado por todo canto.

O cilly resolve isso com uma abordagem que ele chama de witness programs: pequenos trechos de código C que são compilados e executados antes da tradução principal. Esses programas “perguntam” para o compilador C local:

  • Qual o tamanho de um int nessa plataforma?
  • O compilador suporta _Thread_local?
  • Qual o alinhamento de structs?
  • O char é ASCII?
  • Inteiros usam complemento de dois?

Com essas respostas, o cilly ajusta o código gerado para funcionar corretamente naquele compilador específico. É como um handshake entre o Rust e o C antes de começar o trabalho de verdade.


// Exemplo simplificado de witness program
#include <stdio.h>
int main() {
    printf("sizeof_int=%zu\n", sizeof(int));
    printf("sizeof_ptr=%zu\n", sizeof(void*));
    printf("alignof_double=%zu\n", _Alignof(double));
    return 0;
}

O compilador C local roda esse programa, e o cilly usa a saída para calibrar toda a geração de código. Engenhoso.

Compilando o compilador: a demonstração

A prova de conceito mais impressionante do crustc é a auto-compilação. O projeto pega o rustc nightly 1.98.0 e traduz todo o compilador para C. Quando você compila esse C com GCC, obtém um binário funcional que compila código Rust.

Não é um subconjunto do Rust. É o compilador inteiro. Ele compila a standard library, o core, o alloc, tudo.

O processo de build é direto:


# Clone o repositório
git clone https://github.com/FractalFir/crustc
cd crustc

# Compile com GCC (20 jobs paralelos)
make -j20

# Resultado: um compilador Rust funcional
./crustc seu_programa.rs

No hardware de teste do desenvolvedor, o build levou cerca de 78 segundos com 20 jobs paralelos. Isso para compilar 46 milhões de linhas de C. O GCC fez um trabalho respeitável.

Um detalhe que vale mencionar: o desenvolvedor avisa explicitamente para não habilitar otimizações do GCC. Existem bugs na tradução que só aparecem com -O2 ou superior, e o tempo de compilação explode. Por enquanto, -O0 é o caminho seguro.

Por que 46 milhões de linhas?

Quarenta e seis milhões de linhas parece absurdo, e é. Mas tem uma explicação.

O rustc original não é exatamente pequeno. O compilador Rust, incluindo todas as suas dependências, já é um projeto massivo. Quando você traduz código de alto nível (com generics, traits, macros, pattern matching) para C, acontece uma explosão combinatória. Cada instanciação de generic vira código separado. Cada match arm vira uma cadeia de if-else. Abstrações de custo zero no Rust viram custo real no C.

Além disso, o código C gerado não é feito para humanos lerem. Ele é um artefato de compilação, como assembly com sintaxe de C. Variáveis têm nomes como _v432_tmp_ref_0x7fa3. Funções têm assinaturas quilométricas. É código que existe para ser compilado, não para ser entendido.

Um comentarista no Hacker News resumiu bem: “esse código C é mais parecido com um binário do que com código-fonte.” E é uma observação justa. Você não vai abrir esses arquivos para debugar manualmente. Mas o ponto é que um compilador C consegue processá-los.

Transparência de rede: compilação remota via TCP

Uma das features mais curiosas do cilly é a transparência de rede. O backend pode se comunicar com compiladores C remotos via TCP.

Na prática, isso significa que você pode rodar o frontend do Rust na sua máquina moderna e enviar o código C gerado para um compilador rodando em hardware especializado, em outra máquina, possivelmente em outra arquitetura.

O caso de uso concreto: o desenvolvedor demonstrou compilação de Rust para Plan 9 (o sistema operacional da Bell Labs) rodando em uma VM x86, com o frontend executando em um host ARM64 Linux. Dois sistemas operacionais diferentes, duas arquiteturas diferentes, conectados por TCP.

Isso abre portas para cenários que antes eram impossíveis. Quer compilar Rust para aquele mainframe dos anos 90 que só tem um compilador C proprietário? Conecta via rede e manda ver.

O elefante na sala: ABI compatibility

A compatibilidade de ABI (Application Binary Interface) entre o código gerado pelo crustc e o código gerado pelo rustc padrão é descrita como “mostly compatible”. Esse “mostly” esconde um problema real.

No ARM64, por exemplo, o Rust usa uma convenção de retorno de structs que não é representável em C. Quando uma função retorna uma struct pequena (menos de 16 bytes), o Rust usa um registrador dedicado para o ponteiro de retorno. C não tem como forçar esse comportamento.

Aspecto crustc rustc padrão
——— ——– ————–
Código gerado C (via GCC) LLVM IR (via LLVM)
ABI x86_64 Compatível Nativo
ABI ARM64 Parcialmente compatível Nativo
Otimizações Limitadas (bugs com -O2) Completas
Plataformas suportadas Qualquer uma com compilador C Somente com backend LLVM

Na prática, isso significa que você não pode misturar livremente bibliotecas compiladas com crustc e com rustc padrão no ARM64. Em x86_64, a situação é melhor, mas ainda requer testes.

Bootstrapping: o sonho do Guix (e o limite)

Uma das primeiras reações da comunidade foi perguntar: “isso resolve o problema de bootstrapping do Rust?”

Para quem não está familiarizado, o Rust tem um problema de ovo e galinha. Para compilar o rustc, você precisa de um rustc. A cadeia de bootstrap oficial começa com um binário pré-compilado, o que incomoda projetos como o GNU Guix, que buscam builds 100% reproduzíveis a partir de código-fonte.

O crustc, em teoria, poderia servir como caminho alternativo de bootstrap: em vez de baixar um binário do rustc, você compila o código C gerado pelo cilly com GCC.

Mas na prática, o código C gerado é tão denso e opaco que não oferece vantagem real para auditoria. Você estaria trocando confiança em um binário por confiança em 46 milhões de linhas de C gerado automaticamente. Nenhum humano vai auditar isso.

Onde o crustc brilha de verdade para bootstrapping é na diverse double-compilation: compilar o rustc por dois caminhos independentes (LLVM e cilly/GCC) e comparar os resultados. Se ambos produzem o mesmo output, é muito improvável que exista um backdoor no compilador. É o teste de Ken Thompson na prática.

Limitações que ninguém quer ouvir

Seria desonesto não falar dos problemas. O crustc é um projeto impressionante, mas está longe de ser produção-ready.

Memória: o rustc precisa de muita RAM para compilar projetos não-triviais. Mesmo gerando código C, o compilador resultante ainda consome memória como o rustc original. Se sua plataforma legado tem 64 MB de RAM, esqueça.

Undefined behavior: Rust tem operações perfeitamente definidas que mapeiam para undefined behavior em C. Comparação arbitrária de ponteiros, wraparound de inteiros, essas coisas. O cilly precisa emular esses comportamentos de forma segura, o que adiciona overhead e complexidade.

Código platform-specific: o C gerado é específico para um compilador e plataforma. Código gerado para ARM64 Linux não roda em RISC-V32. Você precisa gerar novamente para cada target. Isso não é um bug, é uma limitação fundamental da abordagem.

Status do projeto: o cilly não foi lançado oficialmente. O desenvolvedor menciona três barreiras: um novo emprego, a tese da universidade e uma lesão na mão. O crustc é um teaser do que é possível, não uma ferramenta pronta para uso.

Contexto histórico: não é a primeira vez

O crustc não é a primeira tentativa de traduzir o compilador Rust para outra linguagem. O projeto mrustc já fez algo similar, implementando um compilador Rust alternativo em C++ capaz de compilar o rustc sem precisar de um rustc pré-existente.

A diferença é que o mrustc é uma reimplementação manual, enquanto o crustc é uma tradução automática. O mrustc requer que alguém acompanhe manualmente as mudanças no Rust. O crustc, em teoria, pode ser regenerado automaticamente para cada nova versão do rustc.

Outro projeto relacionado é o rustc_codegen_clr, do mesmo desenvolvedor, que traduz Rust para CIL (o intermediate representation do .NET). O cilly evoluiu desse trabalho. FractalFir claramente tem uma obsessão saudável por fazer Rust rodar em lugares onde não deveria.

O que isso significa para o ecossistema Rust

O impacto mais imediato do crustc é psicológico. A objeção “Rust não roda no meu hardware” fica significativamente mais fraca quando existe uma prova de conceito funcional traduzindo o compilador inteiro para C.

Para projetos práticos, o cenário mais realista é compilação cruzada para plataformas exóticas. Imagine ter um pipeline de CI que compila seu projeto Rust para ARM64, x86_64, RISC-V e, agora, para aquela plataforma obscura que só tem um compilador C dos anos 2000.

A transparência de rede do cilly torna isso especialmente interessante. Você não precisa instalar nada na plataforma target além do compilador C. O cilly pode enviar o código pela rede, compilar remotamente e coletar o binário.

Para a comunidade de segurança, a diverse double-compilation é talvez o uso mais valioso. Poder verificar que o compilador Rust não contém backdoors, compilando por dois caminhos completamente independentes (LLVM e GCC), é um avanço concreto na segurança da cadeia de supply chain de software.

Testando você mesmo

Se quiser experimentar, o repositório está público no GitHub. Você vai precisar de:

  • GCC 13.3.0 ou superior
  • GNU make
  • A libLLVM correspondente (libLLVM.so.22.1-rust-1.98.0-nightly)


git clone https://github.com/FractalFir/crustc
cd crustc
make -j$(nproc)

Prepare-se para o GCC mastigar 46 milhões de linhas. No hardware do desenvolvedor (com 20 cores), levou uns 78 segundos. Na sua máquina, sua milhagem pode variar.

Um aviso: existe um bug documentado que faz o compilador resultante crashar se você executá-lo dentro do diretório de build. Mova o binário para outro lugar antes de usar. O desenvolvedor não sabe por que isso acontece. Bem-vindo ao mundo dos compiladores.

E agora?

O crustc prova que a tradução automática de Rust para C é possível e prática. Não perfeita, não otimizada, mas funcional. É o tipo de projeto que muda a conversa.

A pergunta que fica é se a comunidade Rust vai abraçar essa abordagem como um backend legítimo ou tratá-la como curiosidade acadêmica. Se o cilly amadurecer e ganhar suporte oficial, o Rust pode se tornar viável em plataformas que até agora estavam fora do alcance.

Enquanto isso, FractalFir está lá, no 14o. protótipo de um compilador que traduz Rust para C, com a mão machucada, escrevendo tese e começando emprego novo. Se isso não é dedicação à causa, eu não sei o que é.

O repositório está em github.com/FractalFir/crustc. Vale a estrela.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts