L1VM - go far beyond
L1VM - go far beyond
Here I will show how to use inline assembly in combination with JIT-compiling.
This makes it possible to run programs as fast as a C program or another compiled language.
I will show the program double-test-jit
it can be found here: benchmark 04.
Here are the variable definitions:
#include <intr.l1h>
(main func)
(set int64 1 zero 0)
(set double 1 x 23.0)
(set double 1 y 42.0)
(set double 1 z 7.0)
(set double 1 a 1.0)
(set int64 1 max 10000001Q)
(set int64 1 one 1)
(set int64 1 time 0)
(set string s timerstr "JIT-compiler compile time: ")
So they are set as in every other program. Nothing special there.
Now the inline assembly starts with: (ASM)
(ASM)
loada zero, 0, I0
loadd x, 0, F1
loadd y, 0, F2
loadd z, 0, F3
loadd a, 0, F4
loadd a, 0, F10
loadd y, 0, F22
loadd x, 0, F20
loada one, 0, I4
loada max, 0, I5
loada one, 0, I6
loadl :jit, I40
loadl :jit_end, I41
loada timerstraddr, 0, I50
intr0 6, I50, 0, 0
// start timer
intr0 24, 0, 0, 0
// run jit compiler
intr0 253, I40, I41, 0
// stop timer
intr0 25, I50, 0, 0
With loada
an int64 variable is load into the register as set by the third argument. It’s the same on loadd
for double numbers.
So: loadd x, 0, F1
loads the variable x into the F1 register. All other opcodes later use this registers we have initialized yet!
The loadl
opcodes set the JIT-compiler start and end labels into the registers I40
and I41
.
And this intr0 253, I40, I41, 0
calls the JIT-compiler.
The next part is our main loop where the sum gets calculated by the add
opcodes.
:loop
// call jit code
intr0 254, I0, 0, 0
// jump to following non-jit code
jmp :next
:jit
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F20, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F22, F10
addd F10, F22, F10
:jit_end
addd F10, F22, F10
// store
:next
Here we have the call to the JIT-code by: intr0 254, I0, 0, 0
. And the next line is very important for us. The program jumps over the JIT-code to the label :next
!
As you may have already guessed the add
opcodes do the double number adding.
Now the last part is to calculate the loop the program is running in:
addi I4, I6, I4
lseqi I4, I5, I30
jmpi I30, :loop
intr0 5, F10, 0, 0
intr0 7, 0, 0, 0
intr0 255, 0, 0, 0
(ASM_END)
(funcend)
The lseqi I4, I5, I30
checks if the current loop is less or equal to the max loop setting. If this is true the program will do the jump in the next line to :loop
.
The loop exits if the max loop count is reached. The intr0 5, F10, 0, 0
prints the final result of the add loop. And the next line prints just a new line.
The program exits by intr0 255, 0, 0, 0
. And the (ASM_END)
marks the inline assembler end there.