This is the source code of an RPN calculator with a simple “Just In Time” (JIT) compiler.
This project contains the source code for an RPN calculator implementing a virtual machine with bytecode and supporting “Just In Time” compilation at runtime.
The internal JIT compiler currently only supports the Linux x86-64 ABI.
This project is for educational purpose only as support for a technical conference about compilers and JIT compilation.
This project comes with absolutely no warranty of any kind. You have been warned!
Please read the file doc/COPYING for full licence terms.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
├── bin [may contain some useful scripts]
├── doc [may contain some useful documents]
├── etc [may contain some useful files]
├── lib [may contain some useful libraries]
└── src [top src directory]
└── rpncalc [the rpn calculator source code]
A Makefile
can be found in the src/rpncalc
directory in order to build
/ clean
/ check
the program.
In order to build the program, you must call make
with the all
target:
make
or
make all
If you are using GNU Make
, you can run the Makefile
in parallel to speedup the build:
make -j{number-of-parallel-jobs}
In order to clean the build, you must call make
with the clean
target:
make clean
In order to execute some example tests, you must call make
with the check
target:
make check
Usage: rpncalc.bin [OPTIONS...] [EXPR [VERB]]...
Options:
--help, -h display this help and exit
--verbose, --quiet enable/disable all log messages
--debug, --no-debug enable/disable debug log level
--trace, --no-trace enable/disable trace log level
--print, --no-print enable/disable print log level
--alert, --no-alert enable/disable alert log level
--error, --no-error enable/disable error log level
Expr:
a valid RPN expression
Verb:
execute execute an RPN expression
compile compile an RPN expression
run{=n} run the compiled expression <n> times
clear clear the stack
This RPN calculator implements a simple syntax to compute some mathematical or logical operations, manage the stack of operands and manage the array of registers.
Description | |||
---|---|---|---|
nop |
no operation; do absolutely nothing | ||
nnn |
push the nnn value onto the stack |
||
i1 |
top |
get the topmost value from the stack | |
i1 |
pop |
pop the topmost value from the stack | |
clr |
clear the stack | ||
i1 |
dup |
duplicate i1 |
|
i1 |
i2 |
xch |
exchange i1 and i2 |
i1 |
i2 |
sto |
store i1 into i2 register |
i1 |
rcl |
fetch i2 from i1 register |
|
i1 |
abs |
compute the absolute value of i1 |
|
i1 |
neg |
compute the negation of i1 |
|
i1 |
i2 |
add |
compute the sum of i1 and i2 |
i1 |
i2 |
sub |
compute the difference of i1 and i2 |
i1 |
i2 |
mul |
compute the product of i1 and i2 |
i1 |
i2 |
div |
compute the division of i1 and i2 |
i1 |
i2 |
mod |
compute the remainder of i1 and i2 |
i1 |
cpl |
compute the one's complement of i1 |
|
i1 |
i2 |
and |
compute the logical and between i1 and i2 |
i1 |
i2 |
ior |
compute the logical inclusive or between i1 and i2 |
i1 |
i2 |
xor |
compute the logical exclusive or between i1 and i2 |
i1 |
i2 |
shl |
shift i1 i2 bits to the left |
i1 |
i2 |
shr |
shift i1 i2 bits to the right |
i1 |
inc |
increment i1 |
|
i1 |
dec |
decrement i1 |
|
i1 |
hlt |
blocks the execution for at least i1 millisecond |
Description | |||
---|---|---|---|
i1 |
i2 |
+ |
same as <i1> <i2> add |
i1 |
i2 |
- |
same as <i1> <i2> sub |
i1 |
i2 |
* |
same as <i1> <i2> mul |
i1 |
i2 |
/ |
same as <i1> <i2> div |
i1 |
i2 |
% |
same as <i1> <i2> mod |
i1 |
i2 |
~ |
same as <i1> <i2> cpl |
i1 |
i2 |
& |
same as <i1> <i2> and |
i1 |
i2 |
| |
same as <i1> <i2> ior |
i1 |
i2 |
^ |
same as <i1> <i2> xor |
i1 |
i2 |
<< |
same as <i1> <i2> shl |
i1 |
i2 |
>> |
same as <i1> <i2> shr |
i1 |
++ |
same as <i1> inc |
|
i1 |
-- |
same as <i1> dec |
Description | |||
---|---|---|---|
i1 |
st0 |
same as <i1> 0 sto |
|
i1 |
st1 |
same as <i1> 1 sto |
|
i1 |
st2 |
same as <i1> 2 sto |
|
i1 |
st3 |
same as <i1> 3 sto |
|
i1 |
st4 |
same as <i1> 4 sto |
|
i1 |
st5 |
same as <i1> 5 sto |
|
i1 |
st6 |
same as <i1> 6 sto |
|
i1 |
st7 |
same as <i1> 7 sto |
|
i1 |
st8 |
same as <i1> 8 sto |
|
i1 |
st9 |
same as <i1> 9 sto |
|
rc0 |
same as 0 rcl |
||
rc1 |
same as 1 rcl |
||
rc2 |
same as 2 rcl |
||
rc3 |
same as 3 rcl |
||
rc4 |
same as 4 rcl |
||
rc5 |
same as 5 rcl |
||
rc6 |
same as 6 rcl |
||
rc7 |
same as 7 rcl |
||
rc8 |
same as 8 rcl |
||
rc9 |
same as 9 rcl |
||
rnd |
same as 29 rcl ; get a pseudo-random number |
||
now |
same as 30 rcl ; get the number of milliseconds since epoch |
||
i1 |
i2 |
fib |
same as dup 20 sto add 20 rcl xch ; compute an iteration of the fibonacci suite |
run |
run the compiled expression |
This RPN calculator has a stack of unlimited integer operands.
Description | |||
---|---|---|---|
nnn |
push the value nnn onto the stack |
||
i1 |
top |
get the topmost value from the stack | |
i1 |
pop |
pop the topmost value from the stack | |
clr |
clear the stack | ||
i1 |
dup |
duplicate i1 |
|
i1 |
i2 |
xch |
exchange i1 and i2 |
This RPN calculator has an array of 32 integer registers that can be used to store/fetch temporary or special values.
Description | |||
---|---|---|---|
i1 |
i2 |
sto |
store i1 into i2 register |
i1 |
rcl |
fetch i2 from i1 register |
|
st0 |
same as 0 sto |
||
st1 |
same as 1 sto |
||
st2 |
same as 2 sto |
||
st3 |
same as 3 sto |
||
st4 |
same as 4 sto |
||
st5 |
same as 5 sto |
||
st6 |
same as 6 sto |
||
st7 |
same as 7 sto |
||
st8 |
same as 8 sto |
||
st9 |
same as 9 sto |
||
rc0 |
same as 0 rcl |
||
rc1 |
same as 1 rcl |
||
rc2 |
same as 2 rcl |
||
rc3 |
same as 3 rcl |
||
rc4 |
same as 4 rcl |
||
rc5 |
same as 5 rcl |
||
rc6 |
same as 6 rcl |
||
rc7 |
same as 7 rcl |
||
rc8 |
same as 8 rcl |
||
rc9 |
same as 9 rcl |
||
rnd |
same as 29 rcl , pseudo-random number generator |
||
now |
same as 30 rcl , number of milliseconds since epoch |
Some of theses registers are reserved for special purpose like getting the current time or a pseudo-random number.
Register | Read | Write |
---|---|---|
0 | user-defined | user-defined |
1 | user-defined | user-defined |
2 | user-defined | user-defined |
3 | user-defined | user-defined |
4 | user-defined | user-defined |
5 | user-defined | user-defined |
6 | user-defined | user-defined |
7 | user-defined | user-defined |
8 | user-defined | user-defined |
9 | user-defined | user-defined |
10 | user-defined | user-defined |
11 | user-defined | user-defined |
12 | user-defined | user-defined |
13 | user-defined | user-defined |
14 | user-defined | user-defined |
15 | user-defined | user-defined |
16 | user-defined | user-defined |
17 | user-defined | user-defined |
18 | user-defined | user-defined |
19 | user-defined | user-defined |
20 | calc-reserved | calc-reserved |
21 | calc-reserved | calc-reserved |
22 | calc-reserved | calc-reserved |
23 | calc-reserved | calc-reserved |
24 | calc-reserved | calc-reserved |
25 | calc-reserved | calc-reserved |
26 | calc-reserved | calc-reserved |
27 | calc-reserved | calc-reserved |
28 | calc-reserved | calc-reserved |
29 | calc-reserved | calc-reserved |
30 | get a new pseudo-random number | set a new pseudo-random seed |
31 | current time in ms since epoch | do nothing |
Execute a simple expression.
- Execute the expression
1 + 2
.
rpncalc.bin '1 2 add' execute
Results:
🟢 executing expression <1 2 add>
🟢 result is 3
Compile and execute a simple expression.
- Compile the expression
3 * 4
. - Execute the compiled expression.
rpncalc.bin '3 4 mul' compile run
Results:
🟢 compiling expression <3 4 mul>
🟢 no result <empty stack>
🟢 running the compiled expression...
🔵 the bytecode has never been translated, executing bytecode and translating to machine code...
🟣 emit <function prolog>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <mul>
🟣 emit <mul>
🟣 emit <function epilog>
🟢 result is 12
Compile an equation then execute it with different parameters.
- Compile the equation
y = a * x + b
. - Execute
clear stack
then the compiled equation witha = 1
,x = 2
,b = 3
. - Execute
clear stack
then the compiled equation witha = 2
,x = 3
,b = 4
.
rpncalc.bin 'mul add' compile 'clr 3 2 1 run' execute 'clr 4 3 2 run' execute
Results:
🟢 compiling expression <mul add>
🟢 no result <empty stack>
🟢 executing expression <clr 3 2 1 run>
🔵 the bytecode has never been translated, executing bytecode and translating to machine code...
🟣 emit <function prolog>
🟣 exec <mul>
🟣 emit <mul>
🟣 exec <add>
🟣 emit <add>
🟣 emit <function epilog>
🟢 result is 5
🟢 executing expression <clr 4 3 2 run>
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 10
Compile the Fibonacci function then execute 15 iterations.
- Compile the function
fib
. - Initialize the stack with
0
and1
. - Execute 15 iterations of the Fibonacci function.
rpncalc.bin 'fib' compile '0 1' execute run=15
Results:
🟢 compiling expression <fib>
🟢 no result <empty stack>
🟢 executing expression <0 1>
🟢 result is 1
🟢 running the compiled expression...
🔵 the bytecode has never been translated, executing bytecode and translating to machine code...
🟣 emit <function prolog>
🟣 exec <dup>
🟣 emit <dup>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <sto>
🟣 emit <sto>
🟣 exec <add>
🟣 emit <add>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <rcl>
🟣 emit <rcl>
🟣 exec <xch>
🟣 emit <xch>
🟣 emit <function epilog>
🟢 result is 1
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 2
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 3
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 5
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 8
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 13
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 21
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 34
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 55
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 89
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 144
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 233
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 377
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 610
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 987
Generate a sequence of random numbers.
- Initialize the pseudo-random number generator with the current time.
- Compile the expression to obtain a pseudo-random number between 0 and 127.
- Generate 10 pseudo-random numbers.
rpncalc.bin 'now 30 sto' execute 'rnd abs 127 mod' compile run=10
Results:
🟢 executing expression <now 30 sto>
🟢 no result <empty stack>
🟢 compiling expression <rnd abs 127 mod>
🟢 no result <empty stack>
🟢 running the compiled expression...
🔵 the bytecode has never been translated, executing bytecode and translating to machine code...
🟣 emit <function prolog>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <rcl>
🟣 emit <rcl>
🟣 exec <abs>
🟣 emit <abs>
🟣 exec <i64>
🟣 emit <i64>
🟣 exec <mod>
🟣 emit <mod>
🟣 emit <function epilog>
🟢 result is 71
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 19
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 95
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 107
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 49
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 9
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 43
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 29
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 21
🟢 running the compiled expression...
🔵 the bytecode has already been translated, executing the generated machine code...
🟢 result is 44