- Dozer es un compilador de Rust basado en C puro que busca permitir usar Rust en una etapa más temprana del bootstrap; se está escribiendo sin C++,
flex, yacc ni Makefile
- El compilador oficial rustc está escrito en Rust, por lo que las versiones nuevas se construyen con versiones anteriores de rustc; esta cadena se remonta al compilador inicial de Rust escrito en OCaml y a capas de Guile y C
- El bootstrap de Linux de Bootstrappable Builds parte de una semilla binaria de 512 bytes y se expande hacia compiladores simples, shell, un subconjunto de C, TinyCC, yacc, coreutils, Bash, autotools, GCC y Linux
- Actualmente Rust aparece en una etapa tardía mediante mrustc, escrito en C++, que compila rustc 1.56; por eso es difícil usar Rust antes de introducir C++
- Dozer apunta a ser un compilador de Rust que pueda arrancarse con TinyCC, y luego conectar con libcore, el backend Cranelift de rustc, una herramienta alternativa a cargo y la reconstrucción del rustc/cargo canónico
Objetivos y restricciones de Dozer
- Dozer es un compilador de Rust que se está escribiendo en C puro
- No usa C++, ni tampoco
flex, yacc ni Makefile
- Su objetivo principal es crear un compilador que permita hacer bootstrap de Rust desde C
- En particular, debe poder arrancarse con TinyCC, asumiendo que el sistema no tiene herramientas útiles más allá de un compilador de C y un shell muy básico
El problema de que el compilador de Rust se construya a sí mismo
- Para ejecutar código Rust hay que compilarlo y, por lo general,
cargo build invoca internamente a rustc
- rustc en sí también es un compilador de Rust escrito en Rust, así que un rustc nuevo se compila con una versión anterior de rustc
- rustc 1.80.0 se compila con rustc 1.79.0
- Esta cadena continúa hacia versiones todavía anteriores, como rustc 1.78.0
- Las primeras etapas se remontan a Rust 0.7, momento en el que el compilador estaba escrito en OCaml
- Como también se necesita un compilador de OCaml, la cadena de bootstrap vuelve a depender de implementaciones en otros lenguajes
- camlboot puede compilar un compilador de OCaml usando Guile
- El intérprete de Guile está escrito en C
La parte inferior de la cadena de Bootstrappable Builds
- Bootstrappable Builds trata el flujo para hacer bootstrap de un sistema completo a partir de una pequeña semilla binaria
- El proceso de bootstrap de Linux comienza con una semilla binaria de 512 bytes
- Esta semilla contiene un compilador muy simple que recibe números hexadecimales y emite los bytes crudos correspondientes
- Una lista de bytes hexadecimales, excluyendo comentarios y espacios en blanco, también se considera técnicamente código fuente analizable
- Las etapas siguientes construyen gradualmente herramientas de mayor nivel
- Un sistema operativo muy simple
- Un shell básico
- Un compilador un poco más avanzado
- Una etapa que parece código ensamblador
- Un subconjunto muy básico de C
- Un compilador de C más avanzado escrito en ese subconjunto de C
- Algunas etapas después ya se puede compilar TinyCC, y luego se continúa con
yacc, coreutils básicos, Bash, autotools, GCC y Linux
- Cada etapa está listada en live-bootstrap parts.rst
Rust aparece demasiado tarde en la cadena de bootstrap
- Actualmente Rust aparece muy tarde en este proceso
- La implementación usada es mrustc, una implementación alternativa de Rust escrita en C++
- mrustc puede compilar rustc 1.56 y, a partir de ahí, continuar compilando código Rust moderno
- Sin embargo, para cuando C++ se introduce en la cadena de bootstrap, el bootstrap en la práctica ya está casi terminado
- Para usar Rust en etapas anteriores a la introducción de C++, se necesita un compilador de Rust que pueda arrancarse desde C
Estado actual de la implementación de Dozer
- Dozer lleva unos dos meses de trabajo y se ha escrito sin extensiones
- Actualmente puede compilar sin problemas tanto con TinyCC como con cproc
- El backend usa QBE
- La implementación todavía está en una etapa inicial
- El lexer está completo
- El parser está implementado en buena parte
- La expansión de macros/módulos se está postergando todo lo posible
- La verificación de tipos actualmente solo soporta
i32
- La generación de código todavía está en estado rudimentario
- Actualmente puede compilar con éxito el siguiente código Rust
fn rust_main() -> i32 {
(2 - 1) * 6 + 3
}
Plan para llegar hasta rustc
- El objetivo es desarrollar Dozer gradualmente para que compile ejemplos básicos que usen
libc, y luego compile libcore y rustc
- Para compilar rustc se planea usar el backend Cranelift
- El backend Cranelift está escrito completamente en Rust
- Como se asume que no hay C++, LLVM no puede compilarse
- También se planea crear una herramienta alternativa a cargo que permita compilar paquetes de Rust con Dozer
- Habrá que encontrar y eliminar archivos generados automáticamente dentro del código fuente de rustc
- Las reglas del proyecto Bootstrappable no permiten código generado automáticamente
- El objetivo final es compilar rustc y
cargo, y luego usar ese rustc/cargo compilado directamente para volver a compilar el rustc/cargo canónico
- El proyecto es la tarea más difícil que ha asumido hasta ahora, y la postura es seguir intentándolo aunque existan dudas sobre si será posible completarlo
1 comentarios
Opiniones de Hacker News
Si fuera a bootstrappear Rust, creo que haría en C un proto-Rust con menos funcionalidades que Rust completo, y con ese proto-Rust escribiría un compilador de Rust completo
Por ejemplo, proto-Rust no tendría borrow checker, tendría soporte limitado o nulo para macros, quizá ni siquiera liberaría memoria, y tampoco necesitaría generar buen código
En la práctica sería más parecido a C con sintaxis de Rust, pero desde la perspectiva de un entusiasta de Rust, eso parece mejor que escribir un compilador de Rust en el “C con sintaxis de C” al que apunta este proyecto
Me pregunto por qué no eligieron ese camino
Quitar el borrow checker no rompe los programas correctos; solo permite que se compile una gran cantidad de programas incorrectos
El uso principal de mrustc es compilar rustc, y como ya sabemos que rustc puede compilarse a sí mismo sin errores del borrow checker, no hay problema
Como el compilador de Scala genera código ineficiente, después recompilan el compilador real con él mismo
Así al final obtienen un compilador real eficiente que genera buen código, y este proceso forma parte de la compilación estándar del lenguaje
https://github.com/mozart/mozart2
Como hobby estoy haciendo un compilador de C en Rust, y lo llamo Small C Compiler como broma de que Rust obviamente es más pesado que C. Es una parodia de “Tiny C Compiler”
Uso Cranelift como backend, pero estoy haciendo que la estructura completa del compilador se pueda enchufar y cambiar con muchos traits, y que sea fácil de hackear
No pienso publicarlo como open source hasta que funcione lo suficiente como para manejar
printf("%s", "Hello World!")Intenté implementar el preprocesador y el parser, y por el infame problema de typedef terminé involucrándome también con rust-peg y HimeCC
Sé que en la industria se usan tablas de símbolos para mantener el contexto de typedef, pero eso tenía la limitación de no poder leer los tipos que vienen más abajo. Me pregunto cuál será la solución académica; lo único que se me ocurre es memoria transaccional
Si llega a haber algo útil, probablemente terminaré publicándolo
Más tarde James Hendrix lo expandió en un libro con una implementación más completa. De niño encontré ese libro en la mesa de ofertas de CompUSA, y gracias a eso aprendí C; todavía lo conservo
https://archive.org/details/dr_dobbs_journal_vol_05_201803/p...
https://www.amazon.com/Small-Compiler-Language-Theory-Design...
Es realmente genial, y lo interesante es que el mismo tipo de problema de bootstrapping existe también en el hardware
¿Qué fabrica las computadoras? Computadoras fabricadas anteriormente y el software que corre sobre ellas. Mientras más lo piensas, más interesante se vuelve
Hace unos meses conocí a alguien que trabaja en una startup de entrega/fulfillment de materiales para proyectos de construcción
Este tipo de trabajo requiere una especialización distinta a la de la logística general tipo Amazon, no solo porque los materiales suelen tener propiedades extrañas o peligrosas, sino también porque muchas veces el destino de entrega todavía ni siquiera tiene dirección
Se puede resolver, pero parece requerir una especialización que va más allá de las capacidades habituales de las empresas modernas de entrega
La razón era que, al colaborar con empresas europeas, necesitábamos demostrarles a los reguladores que no había backdoors
Era un problema muy interesante pero muy difícil; nuestro equipo participó solo de forma indirecta, trabajando en pasar datos a través de un proxy que garantizara que todos los datos fueran auditables y que no se enviara nada que no debiera enviarse
Me fui de la empresa antes de que terminara, y después escuché que lo descartaron porque era demasiado difícil
Luego apareció x86 sin un presupuesto enorme ni grandes compradores, y diseñaron el ensamblador para que fuera lo más eficiente y denso posible
Como resultado, se perdieron características que en otras máquinas se podían tener cómodamente
En teoría, podrías construir tú mismo una computadora muy simple solo con componentes individuales
Sería grande, ineficiente y extremadamente lenta, pero podrías hacer que siguiera una arquitectura de conjunto de instrucciones específica, y sobre ella construir un programa de bootstrapping
Entonces podrías afirmar que el resultado obtenido en una mala computadora completamente comprensible es el mismo que el obtenido en hardware moderno en el que no confías por completo
Es una especie de problema de bootstrapping. Por ejemplo, los yacimientos actuales de petróleo son más difíciles de explotar que hace 100 años, y me pregunto si podríamos volver a bootstrappear hasta llegar ahí
Me molestó un poco tener que seguir enlaces 4 veces para encontrar una justificación de alto nivel que explicara los beneficios del bootstrapping
Esperaba que la parte de “Why” del título cubriera eso
https://bootstrappable.org/benefits.html
La seguridad es una razón importante, y es el punto que el equipo de bootstrappable suele destacar
Para evitar el problema de trusting trust y ataques como la reciente puerta trasera de xz, hay que poder hacer bootstrap de todo a partir de código fuente puro
Ellos incluso eliminan todos los archivos pregenerados para depender solo de cosas escritas a mano y auditables. Por ejemplo, el bootstrapping de Python se vuelve bastante complejo porque el código fuente incluye código generado por scripts de Python
A mí, en cambio, me interesa más el aspecto de preservación cultural. Quiero preservar medios modernos en lugares como el Arctic World Archive para los arqueólogos del futuro, pero no tiene sentido si no hay forma de decodificarlos
Podemos preservar las especificaciones, pero no podemos esperar que implementen x265 y todo lo necesario desde cero. Si preservamos binarios, tendrían que hacer funcionar hardware de mil años de antigüedad o virtualizar una CPU de hace mil años
También podríamos darles una definición simple de Lisp y código que corra encima, pero ¿quién va a implementar x265 en Lisp básico? No es realista
Por eso, en mi proyecto creé una máquina virtual simple e hice bootstrap de C sobre ella
Es muy fácil de portar no solo a arquitecturas actuales, sino también a arquitecturas futuras o extraterrestres. Arqueólogos del futuro o una civilización extraterrestre podrían implementar la VM en un día, ejecutar el bootstrap de C encima y luego compilar ffmpeg y demás para decodificar nuestros medios
No hay cajas negras; todo es código fuente abierto, escrito a mano, depurable y auditable
https://github.com/ludocode/onramp?tab=readme-ov-file#why-bo...
https://en.wikipedia.org/wiki/Arctic_World_Archive
Me resulta un poco confuso. Recién a mitad del artículo aparece la razón por la que empezó el recorrido del título, y el punto central es que, para cuando C++ entra en la cadena de bootstrapping, el bootstrap básicamente ya terminó, así que aunque uno quisiera usar Rust antes de eso, no hay manera
Entonces parece que la idea es que estaría bueno tener un compilador de Rust escrito en C, específicamente uno que pueda bootstrapppearse desde TinyCC en un sistema donde se supone que todavía no hay herramientas útiles
Pero eso contradice la premisa inicial. rustc 1.80.0 se compila con 1.79.0, 1.79.0 con 1.78.0, y así sucesivamente hasta 0.7; el compilador de ese momento estaba escrito en OCaml
Además, se mencionaba que existe un proyecto que compila con éxito un compilador de OCaml usando Guile, y que el intérprete de Guile está escrito en C
Entonces el camino sin C++ que el autor quiere ya existe; simplemente no es el camino que el equipo de rustc usa en el día a día
Al final, la motivación no queda clara. No sé si quiere crear un proceso de bootstrapping basado en C mejor, convertirlo en el método cotidiano de bootstrapping de rustc, por qué quiere eliminar el paso de C++ o por qué prefiere el paso por C
Si lo hace simplemente porque quiere, está bien, pero después de leer un artículo bastante largo, no me queda claro ningún otro propósito
Cada etapa tarda horas, y como 1.80 requiere 1.79, 1.79 requiere 1.78, y así hasta 0.7, no se puede saltar ninguna etapa
Incluso totalmente automatizado, este bootstrap podría tardar meses
Además, tengo entendido que las primeras versiones de rustc solo generaban salida para LLVM, así que de todos modos habría que bootstrapppear un compilador de C++ para compilar LLVM
Si ya tienes un compilador de C++, simplemente puedes compilar mrustc. Actualmente mrustc solo soporta hasta rustc 1.54, así que aun así habría que compilar pasando por unas 35 versiones
Todo este proceso no es práctico. El objetivo de Dozer es hacer bootstrap de un compilador de C pequeño, compilar Dozer y luego compilar directamente el rustc moderno
Así se puede obtener Rust directamente, sin hacer bootstrap de C++ ni de etapas intermedias
Si se pudiera separar GCC 4 y binutils de sus scripts de compilación originales, creo que se podría recortar más o menos la mitad de la lista
Gran parte de los elementos ahí son simplemente reconstruir una y otra vez herramientas tipo autoconf y sus dependencias
https://github.com/fosslinux/live-bootstrap/blob/master/part...
No entiendo bien el punto. Para crear un binario nuevo que funcione en la máquina destino, rustc tiene que soportar la arquitectura destino
Si ya agregaste ese soporte a rustc, entonces simplemente haces que rustc se compile a sí mismo
A veces imagino escribir un intérprete o compilador de C++ en Scheme
Ir directamente desde Scheme hasta el GCC actual podría ser un atajo enorme
Pero, según la sabiduría convencional, escribir un compilador de C++ se considera casi imposible. Aun así, creo que serviría para aprender
Si se observa toda la pila desde el subensamblador, ¿podría ser una forma de esquivar el problema de trusting trust?
https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_Ref...
Aun así, existen cosas como https://en.m.wikipedia.org/wiki/Underhanded_C_Contest, y creo que algunas de esas participaciones se me habrían escapado incluso si yo las hubiera auditado.
Cuando estaba aprendiendo un poco de C, busqué cómo hacía la gente cosas tipo C++ en C, y vi implementaciones de objetos, excepciones y concurrencia.
Si mrustc está escrito en C++, ¿no sería más fácil portar a C el código C++ que funciona usando esos primitivos estilo C++ en C?
También parece posible ir migrándolo poco a poco aprovechando la fuerte interoperabilidad entre C++ y C.
Claro que sé que sería un trabajo de portabilidad difícil y lleno de trampas. Pero hay que recordar que el punto de comparación es escribir desde cero un compilador de Rust en C.
También me vienen a la mente los compiladores de C++ a C que existían antes. No sé si todavía existen.
Incluso hoy, creo que serían útiles compiladores de Rust a C/C++ y de C++ a C legible para humanos, porque permitirían combinar las ventajas de seguridad de un lado con el ecosistema de herramientas del otro.