Releases: lionkor/mcl
v2.0.0
- Added optimization step (during compilation). This improves runtime performance by up to 2x (for example in examples/primes.mcl). For comparing performance of your own programs, the optimizer can be turned off with
--dont-optimize
. More details below under "Performance". - Added decompiler, can be used via
--decompile
. More details below under "Decompiler". - Added stack check to the VMs
push()
implementation (in release builds, in debug builds all operations have stack checks). - Fixed various little bugs.
Full Changelog: v1.0.1...v2.0.0
Performance
Performance improvements have been made by adding an optimization step to the compilation to bytecode. This optimization step performs very basic as-if replacement/substitution of instructions. For this, some specialized instructions have been added, which the programmer does not need to use (but may). These are not currently in the Reference.md, but they are:
dup2
: Acts likeover over
, duplicating the two top elements of the stack. For example, a stack of1 2
would become1 2 1 2
.over over
, if found in the code, is automatically replaced withdup2
. This saves some push() and pop(), as it does a simplememmove
in practice, and then moves the stack top.inc
anddec
: Acts likepush 1
followed byadd
, effectively incrementing the stack top by one. The interpreter / VM can increment without moving the stack top by use ofinc
, sopush 1
followed byadd
is replaced by this. In the same way,push 1
followed bysub
is replaced withdec
.jz
andjnz
: These special conditional jump instructions may be the most useful out of the new instructions, as loops frequently check for zero.jz :somewhere
is equivalent topush 0
followed byje :somewhere
, andjnz
<=>jn
in the same way. This, again, can save some stack pushing, as the VM is able to simply look at the stack top and determine if it's zero. This is common enough that a lot of architectures have an instruction like this (in some variation).
These replacements can be turned off with the commandline flag --dont-optimize
, which also allows you to confirm that your program is 1) not breaking because of optimizations/replacement, and 2) is actually executing faster.
Decompiler
The new and shiny decompiler can decompile mclb
files back to compilable mcl
. For example, consider the following code:
push 1
:start
push 1
add
dup
dup
print
push 1000
jn :start
halt
This simply counts from 2 to 1000. Compiled & hex-dumped, the binary is:
0000 10 01 00 00 00 00 00 00 03 00 00 00 00 00 00 00
0010 0b 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00
0020 09 00 00 00 00 00 00 00 10 e8 03 00 00 00 00 00
0030 12 01 00 00 00 00 00 00 0a 00 00 00 00 00 00 00
Decompiled with --decompile
:
# decompiled from 'examples/count.mclb'
# all label names are generated pseudo-randomly
push 1
:kiredi # addr=1
inc
dup
dup
print
push 1000
jn :kiredi # ->1
halt
# encountered NOT_AN_INSTRUCTION, assuming end of file
Please note that kiredi
is a randomly generated string, and one will be generated for each label in the code.
This can, of course, be fed back into the compiler to compile to a new executable.
Also note the optimization step at play here: push 1 add
as optimized to inc
.
First Release v1.0.1
First release, see README.md and Reference.md for more information.