El problema fundamental que Cilly aborda es la portabilidad de código Rust a arquitecturas y sistemas operativos que carecen de soporte nativo para LLVM o toolchains Rust modernos. Esto es particularmente relevante para hardware embebido, sistemas legacy o plataformas con compiladores C altamente especializados y a menudo propietarios. Históricamente, la adopción de lenguajes de alto nivel como Rust en estos entornos ha sido limitada por la necesidad de un backend de compilador robusto y adaptable.
Cilly resuelve esto generando código C que es compatible con el compilador C específico del target, incluso si este es no-estándar o muy antiguo. Esto abre la puerta a utilizar Rust en un espectro mucho más amplio de dispositivos, eliminando una barrera significativa para su adopción en dominios donde C ha sido tradicionalmente el único lenguaje viable.
Arquitectura del Sistema
Cilly opera como un backend de compilador (plugin) para rustc. Intercepta la representación intermedia (IR) de Rust y la traduce a código fuente C. La clave de su diseño es un mecanismo de 'witness programs' que genera pequeños fragmentos de código C para sondear las características del compilador C y la plataforma de destino (ej. soporte para _Thread_local, tamaños de tipos, alineaciones, codificaciones de caracteres, formato de enteros como two's complement). Esta información permite a Cilly generar código C altamente adaptado, evitando suposiciones fuera de ANSI C.
La toolchain cilly envuelve a rustc y un compilador C. Desde la perspectiva del usuario, se configura un 'triple' de destino y una definición de herramienta que especifica el compilador C a usar (ej. /usr/bin/sdcc con argumentos como -mz180 --std-c89). Cilly puede operar de forma transparente en red, permitiendo que rustc se ejecute en una plataforma host (ej. Linux Arm64) y se comunique con un compilador C remoto en el target (ej. Plan9 x86) a través de TCP, o incluso UART. El código C generado puede ser ABI compatible con el código Rust compilado por rustc estándar, aunque existen excepciones en plataformas como Arm64 debido a convenciones de paso de argumentos para structs (sret). Cilly también puede generar makefiles y dividir funciones/globales en archivos separados para facilitar la integración con sistemas de build existentes.
Flujo de Compilación con Cilly
- 1 rustc Compila código Rust a IR (Intermediate Representation)
- 2 Cilly Backend Recibe IR de Rust, genera 'witness programs' para sondear el compilador C
- 3 Compilador C (Target) Ejecuta 'witness programs', devuelve características de la plataforma
- 4 Cilly Backend Genera código C adaptado a las características del compilador C
- 5 Compilador C (Target) Compila el código C generado a binario ejecutable para el target
| Capa | Tecnología | Justificación |
|---|---|---|
| compute | rustc | Frontend del compilador Rust, genera la representación intermedia (IR) que Cilly consume. |
| compute | Cilly (Rust library) | Backend del compilador que traduce IR de Rust a código C adaptable a compiladores específicos. vs rustc_codegen_clr, otros intentos privados del autor |
| compute | GCC / SDCC (u otros compiladores C) | Compilador C de destino que compila el código C generado por Cilly a binario ejecutable. -mz180, --std-c89 (ejemplo para SDCC) |
| networking | TCP | Protocolo para comunicación remota entre Cilly (host) y el compilador C (target) para bootstrapping en plataformas sin cross-compilers. vs UART (mencionado como posible extensión) |
Trade-offs
Ganancias
- ▲ Soporte para targets oscuros/legacy
- ▲ Reducción de la barrera de entrada para Rust en sistemas embebidos
- ▲ Flexibilidad de adaptación a compiladores C no estándar
Costes
- ▲ Código C generado es específico del compilador/plataforma
- △ Posibles problemas de compatibilidad ABI con rustc estándar en algunas plataformas (ej. Arm64 sret)
- ▲ Rendimiento de compilación con optimizaciones activadas (en demo)
Fundamentos Teóricos
El concepto de adaptar la generación de código a las características de un compilador de destino no es nuevo y se relaciona con los principios de retargeting de compiladores. Trabajos seminales en la teoría de compiladores, como los de Aho, Sethi y Ullman (conocidos por 'The Dragon Book'), establecieron las bases para la generación de código y la optimización dependiente del target. La idea de 'witness programs' o 'feature detection' se alinea con técnicas de autoconfiguración y portabilidad de software que han existido desde los primeros días de Unix, donde herramientas como autoconf generaban scripts para probar las capacidades del sistema. En un contexto más moderno, la capacidad de Cilly para sondear el entorno de ejecución y el compilador C recuerda a los principios de la 'máquina virtual' o 'lenguaje intermedio' que abstrae las diferencias del hardware subyacente, pero en este caso, la abstracción se realiza a nivel del compilador C y su entorno de ejecución.