Después de un tiempo, conocí el sistema de fuertes sod32 escrito por Lennart Benschop . Este sistema fuerte tiene una máquina virtual escrita en C, y la imagen binaria que se carga en la máquina virtual no depende del orden de bytes en el sistema host de palabras. sod32 tiene 32 primitivas, aquí están:
NOOP
( --- )
Do nothing
SWAP
( x1 x2 --- x2 x1 )
Swap the two top items on the stack.
ROT
( x1 x2 x3 --- x2 x3 x1 )
Rotate the three top items on the stack.
0=
( x --- f)
f is true if and only if x is 0.
NEGATE
( n1 --- -n1)
Negate top number on the stack.
UM*
( u1 u2 --- ud )
Multiply two unsigned numbers, giving double result.
C@
( c-addr --- c)
Fetch character c at c-addr.
@
( a-addr --- x)
Fetch cell x at a-addr.
+
( w1 w2 --- w3)
Add the top two numbers on the stack.
AND
( x1 x2 --- x3)
Bitwise and of the top two cells on the stack.
OR
( x1 x2 --- x3)
Bitwise or of the top two cells on the stack.
XOR
( x1 x2 --- x3)
Bitwise exclusive or of the top two cells on the stack.
U<
( u1 u2 ---- f)
f is true if and only if unsigned number u1 is less than u2.
<
( n1 n2 --- f)
f is true if and only if signed number n1 is less than n2.
LSHIFT
( x1 u --- x2)
Shift x1 left by u bits, zeros are added to the right.
RSHIFT
( x1 u --- x2)
Shift x1 right by u bits, zeros are added to the left.
UM/MOD
( ud u1 --- urem uquot)
Divide the unsigned double number ud by u1, giving unsigned quotient and remainder.
+CY
( n1 n2 cy1 --- n3 cy2)
Add n1 and n2 and the carry flag cy1 (must be 0 or 1) giving sum n3 and carry flag cy2. (n3 cy2 can be seen as a double number)
SCAN1
( x d --- u)
Scan x for the first 1 bit. u is the position of that bit (counted from the scan direction) and 32 if x=0. d is the scan direction, 0 is left-to-right, 1 is right-to-left.
SPECIAL
( x ---)
Any of a large number of special instructions, indicated by x.
DROP
( x ---)
Discard the top item on the stack.
>R
( x ---)
Push x on the return stack.
C!A
( c c-addr --- c-addr)
Store character c at c-addr, keep address.
!A
( x a-addr --- a-addr)
Store cell x at a-addr, keep address.
DUP
( x --- x x )
Duplicate the top cell on the stack.
OVER
( x1 x2 --- x1 x2 x1)
Copy the second cell of the stack.
R@
( --- x)
x is a copy of the top of the return stack.
R>
( --- x)
Pop the top of the return stack and place it on the stack.
0
( --- 0)
The constant number 0.
1
( --- 1)
The constant number 1.
4
( --- 4)
The constant number 4.
LIT
( --- lit)
Push literal on the stack (literal number is in-line).
Me di cuenta de que tenía la oportunidad de encontrar una respuesta a mi pregunta y comencé a convertir las primitivas en definiciones de alto nivel. Quiero señalar de inmediato que toda esta actividad tiene un significado puramente académico. Es poco probable que sea posible aplicar los resultados obtenidos en la práctica debido a la pérdida de rendimiento. Mientras experimentaba, rastreé el tamaño del binario del sistema fuerte y el tiempo de ejecución del conjunto de pruebas de John Hayes. Construí una nueva imagen binaria con el comando
echo 'S" extend-cross.4" INCLUDED '|./sod32 kernel.img
y ejecuté las pruebas de esta manera:
time ./sod32 kernel.img <<< $(echo -e 'S" tester.fr" INCLUDED\n12345\nBYE')
En la siguiente tabla, puede ver cómo cada cambio afectó el tamaño y el rendimiento. Los enlaces de la columna "cambiar" conducen a la confirmación correspondiente en el github.
| el cambio | tamaño kernel.img | tiempo de ejecución tester.fr |
| sod32 original | 10164 | 0 min 0,059 s |
| lshift, rshift | 10312 | 0 min 0,071 s |
| +, um *, um / mod | 11552 | 0 min 0,123 s |
| c @, c! a | 11952 | 0 min 0,795 s |
| 0 =, negar <, u < | 12340 | 0m2.800s |
| soltar | 12784 | 0 min 5,022 s |
| intercambio, pudrición, cambio | 13436 | 0 min 5.860 s |
| sp @, sp!, rp @, rp!, dup | 13680 | 0 min 8,696 s |
| r @, r>,> r | 14160 | 0 min 15,463 s |
| y, o, xor | 14336 | 0m21.198s |
| 0, 1, 4 | 15236 | 0 min 21,671 s |
| 0 rama | 15912 | 0m41.765s |
Como resultado, el tamaño de la imagen binaria del sistema de fuertes aumentó de 10164 a 15912 (+ 57%), el rendimiento se redujo 708 veces (casi 3 órdenes de magnitud). Quizás el rendimiento podría mejorarse perfilando el código y optimizando los cuellos de botella, pero estoy seguro de que el resultado seguirá siendo muy lento en comparación con el sod32 original.
De 32 primitivas más lógica adicional en el intérprete interno, terminé con 7: nop, nand,!, @, Um +, especial, lit. El intérprete interno conserva la lógica para ejecutar primitivas y definiciones de alto nivel (llamada), así como la lógica para completar la definición de alto nivel (siguiente). Encontré la respuesta a mi pregunta: un sistema de fortaleza se puede construir sobre la base de 9 primitivas (u 8, si nop no tiene que ser una primitiva). Estoy confundido por el hecho de que hay hasta 3 primitivas para el acceso a la memoria: @,! e iluminado, pero no he descubierto cómo evitarlo. Bien podría haberme perdido algo, así que si sabe cómo deshacerse de una gran cantidad de primitivas, escriba en los comentarios.