Sistema · en curso
Loment VM
Una segunda forma de ejecutar un programa. El binario puede entregarse a un ejecutor que lo adopta bajo demanda y decide si adoptarlo leyendo lo que el binario ya lleva. No es una ruta más rápida, ni una forma de escribir programas con más facilidad. Ya existe un intérprete funcional en el repositorio — el paquete no lo incluye, y el ajuste no cambia lo que emite el compilador.
Lo que existe y lo que no
El repositorio ahora lleva una capa de intérprete funcional, de T0 a T3, que lee la misma forma intermedia que el enlazador. T0 ejecuta esa forma directamente; T1 registra un perfil de retroalimentación sin cambiar un byte de salida; T2 decodifica las instrucciones antes del bucle de ejecución y corre 6.1× T0 en el corpus con salida idéntica byte a byte; la serialización compacta es el 28.5% de la forma de texto para la misma unidad; T3 añade especialización con guardas y una forma de volver.
Lo que no existe es nada de eso en el paquete. El intérprete es la familia
tools/lvm.py del repositorio, escrita en Python. No está entre los binarios que
envía una versión, así que una herramienta instalada no puede ejecutar un programa de esta
manera. El ajuste tampoco cambia el artefacto: hoy choose vm y no_vm
producen una salida idéntica byte a byte.
Una deuda que el proyecto ya cargó antes. El ajuste runtime estuvo
declarado en el binario un tiempo sin hacer nada, y luego se elevó hasta lo que afirmaba. Esta es
otra vez esa forma: el motor existe, el envío no. La página dice qué mitad es cuál en vez de
dejar que el lector lo descubra.
Lo que no es
Tres lecturas son erróneas, y cada una es una con la que se llega. Van antes del diseño, porque enunciarlas mal convierte esto en algo que intenta hacerlo todo y no hace bien nada.
No es la ruta rápida
Al revés. La ruta nativa de Loment ya cambia velocidad por independencia, y no es rápida
de por sí: frente a C -O2 en el benchmark de arrays de
docs/212
va 10.7× por detrás — una medición registrada, no una
promesa en pie. Una VM es una capa por encima de eso. El eje de esta línea no es
el rendimiento, y la vía de escape es una línea completamente distinta.
No es escribir una vez, ejecutar en cualquier parte
Ese trabajo se le encomendó hace tiempo al tiempo de compilación: un IR, tres salidas — Linux ELF, Windows PE, o un objeto autónomo. Un formato de bytecode no está más cerca de la portabilidad que eso, y la afirmación falla su propia prueba: si la portabilidad fuera el objetivo, el criterio sería "añadir un objetivo no necesita un back end nuevo", y hay dos objetivos.
No es una forma de escribir programas más rápido
Eso es otro conjunto de cosas y esto no toca ninguna de ellas: las otras seis sintaxis de superficie, el formateador, el servidor de lenguaje, las definiciones de sintaxis en tiempo de compilación, la guía escrita para agentes de programación. LVM llega después de que el programa esté escrito.
El diseño, en tres partes
El bytecode no es una invención nueva
Es una serialización de la forma intermedia que el compilador ya emite. El back end incluido es una máquina de pila — una ranura de pila por valor, los argumentos en la pila, los agregados a través de un puntero de resultado oculto — así que ya existe un modelo de ejecución completo para ese subconjunto, y ya se ejercita. Lo que falta es el paso que lo ejecuta. LVM sería un cuarto lector de la misma forma: ningún front end nuevo, ninguna forma intermedia nueva, ningún sistema de tipos nuevo.
Un quinto ajuste
choose ya tenía cuatro — qué capa de la biblioteca puede usar el
programa, cómo recupera memoria, si el binario puede llevar un runtime, y si puede
alcanzar fuera de sí mismo. vm es el quinto, y responde a ¿para qué
modelo de ejecución es este programa? Sin escribirlo es no_vm, que
es lo que todo era antes de que el ajuste existiera. Desde 0.1.4.6 lo acepta también una compilación de
versión; lo que eso compra hoy es una declaración, no un artefacto distinto.
El valor dice si, nunca cuál. Esa es la regla que el ajuste
runtime ya sigue: lo que hay dentro cambiará con los años, y un nombre que
codificara el peldaño de hoy tendría que renombrarse para añadir el de mañana.
La compuerta, y por qué se abrió
Hasta 0.1.4.6, una compilación de versión rechazaba choose vm
en tiempo de compilación; solo el canal de desarrollo lo aceptaba. La regla era tajante —
hasta que exista un motor usable, toda llamada a él es ilegal en una versión — para que una
herramienta instalada no pueda aceptar nunca una promesa que no puede cumplir. Una
promesa que se cae es peor que un rechazo.
0.1.4.6 la abre, porque la premisa del rechazo dejó de ser cierta: llegó el primer motor usable. El ajuste es ahora una declaración en ambos canales, y la corrección se registra en vez de borrarse — la comprobación que exigía un rechazo ahora exige aceptación, así que «esta compuerta existió, y aquí está cuándo se abrió» sigue visible.
La compuerta necesitaba una noción que el repositorio no tenía: ¿este compilador es una compilación de versión o una de desarrollo? El diseño la reduce a una constante, un valor por lado, invertida al empaquetar. Una línea es deliberada — la guía del lenguaje empaquetada se copia a la distribución sin modificar, para que «la copia del paquete es la copia del repositorio» siga siendo comprobable, y una línea es la forma más pequeña de doblar esa regla. El coste se dice con ella: la semilla del compilador enviada ya no es idéntica byte a byte a la del repositorio, así que el criterio baja de idéntica a reproduciblemente igual, y la diferencia es exactamente esa constante.
La escala
Lee la VM como peldaños, no como una sola cosa que construir. Cada peldaño tiene que poder alcanzarse y cada uno tiene que poder refutarse — «escribir un JIT» no es un peldaño, porque nada podría refutarlo. La lista de piezas está tomada de dos sistemas que lo han hecho a escala; la implementación sería del propio proyecto, y si sigue siéndolo es una decisión abierta.
T0 — bytecode y un intérprete directo
El suelo. Construido en el repositorio. Criterio: un programa ejecutado así produce una salida idéntica byte a byte y el mismo código de salida que el artefacto nativo.
T1 — retroalimentación
Perfiles de tipos, ramas y puntos de llamada. Construido en el repositorio. Criterio: el perfil llega al artefacto y un tercero puede leerlo — y apagarlo deja el artefacto idéntico byte a byte.
T2 — un compilador base
Quita el coste de la interpretación sin optimizar. Construido en el repositorio — 6.1× T0 en el corpus. Criterio: mediblemente más rápido que T0, semánticamente idéntico.
T3 — un compilador optimizador con una forma de volver
Una conjetura que resulta equivocada tiene que poder abandonarse. Construido en el repositorio, para la única optimización que carga una conjetura. Ese es el único criterio duro de este peldaño: después de abandonar una conjetura, el resultado del programa coincide con el artefacto nativo.
T4 — recuperación generacional y concurrente
El único peldaño que no es una extensión de los demás. El diseño de recuperación solía decir que esto no podía hacerse aquí en absoluto: el backend guardaba punteros solo en locales y ranuras de parámetros, y eso descarta la barrera de escritura habitual. Esa razón ya no se sostiene — llegó una forma de convertir un puntero en un entero, y la mitad generacional aterrizó en el lado de la recuperación, con una tabla de tarjetas y una barrera de escritura. Lo que sigue por delante es hacerlo dentro de la VM, que es un problema distinto de hacerlo por adelantado.
El repositorio lleva T0 a T3; el paquete no lleva ninguno. Construir el intérprete y enviarlo en la distribución son dos pasos distintos, y solo está hecho el primero. Los peldaños de arriba son la forma del plan, no un calendario.
Para qué sirve
Primero, agua fría: las dos cosas que los proyectos grandes quieren con más frecuencia — compilar más rápido, ejecutar más rápido — están ambas fuera de alcance. El tiempo de compilación es un problema dentro del compilador, y el tiempo de ejecución es el eje equivocado para esta línea.
Pero lo "grande" ya ha expuesto un problema que es estructural más que un defecto, y esto es lo único que puede curarlo. Los símbolos emitidos son planos: cien módulos significa que los nombres de función tienen que volverse únicos a nivel global a mano, y dos módulos que definen cada uno el mismo nombre corto se rechazan. Es una consecuencia de "un solo enlazado estático" como modelo — el clásico muro de escalabilidad de un proyecto grande, empujado directamente a la cara del usuario por la propia estructura del lenguaje. El repositorio ya ejecuta un corpus de proyecto sobredimensionado como criterio, así que esto se mide en lugar de suponerse.
Adoptar un módulo en tiempo de ejecución
Añadir un módulo a un artefacto nativo significa reenlazar todo el programa. Aquí significa cargar una unidad.
Reemplazar uno sin reenlazar
Cambiar un módulo, recargarlo, y dejar el resto del programa donde está.
Ejecutar código que uno no escribió
Una extensión de terceros, o una que escribió una máquina. Aquí es donde las ideas propias del proyecto tienen por primera vez un trabajo que hacer. En tiempo de enlazado, la tabla de puertos, los rangos que una unidad declara que puede alcanzar, el registro de lo que no tiene y sus números de recuperación son auditoría — algo que leer después. En tiempo de carga esos mismos registros se vuelven una compuerta — algo con lo que decidir, antes de que nada se ejecute.
Que es la única frase que esta página respalda, y es falsable: añadir un módulo de extensión no significa reenlazar todo el programa — y antes de cargarlo, se puede saber si puede alcanzar lo que no debería.
Lo que no afirma
El registro de diseño mantiene esta lista, y corresponde a la página más que a él.
Que sea nuevo
Nada en él es un ladrillo nuevo. La evidencia de ejecución llevada en el artefacto y juzgada en tiempo de carga es de los años noventa; negarse a cargar lo que no puede probarse es como funcionan los runtimes en sandbox; una entrada con varios modelos de ejecución es lo que hace el mayor runtime móvil. Lo que podría estar colocado de otra forma es la costura, no las piezas.
Que nadie haya hecho esas dos cosas
La formulación en todo el texto es no encontrado en una búsqueda, que no es lo mismo que no hecho. Dos búsquedas que matarían la afirmación están anotadas como criterios abiertos, con la nota honesta de que no se ejecutaron de forma exhaustiva.
Que sea útil
La caja está vacía hoy. Y el tercer papel de arriba se apoya en un número que nadie ha medido — cuánto de ese corpus sobredimensionado es en realidad código que debería haberse cargado tarde. Si el número sale cerca de cero, el papel es una historia bonita.
Dónde está escrito
vm — y el registro de cuándo se abrió esa compuerta.
docs/243
T0: el bytecode es la forma intermedia existente, y qué significa ejecutarla.
docs/259
T2: sacar la decodificación del bucle de ejecución, y el criterio que cuenta decodificaciones.
docs/250
La forma compacta: la misma forma, el 28.5% del tamaño.
docs/261
T3: especialización con guardas y por qué la desoptimización es barata en una máquina de pila.
Roadmap
Dónde se sitúa la VM frente a las versiones — un plan, enunciado como un plan.
LomentGC
La escala de recuperación — un sistema que sí está construido, para comparar.
Volver a la página principal
Qué es Loment, y las dos clases de programa que construye.