| ...... |
|operation stack above | [...]<- sp
| old_lv |
| old_pc |
| var[2] |
| var[1] |
| var[0] |
| ...... |
| arg[2] |
| arg[1] |
| arg[0] |
| link_ptr | [...]<- lv
| ...... | [2]
| ...... | [1]
| MAIN_FRAM | [0]
table ——>| Cell |
|------|
| Cell |
|------|
| Cell | ——> arrary ——>|[0]|[1]|[2]|... + |mark bit|
|------|
| Cell |
|------|
| Cell |
|-....-|
array reference table ——> |[0]|[1]|[2]|...
I implement two GC mechanisms but the last does not work in our PAD context.
Mark and Sweep: This mechanism will be triggered when the heap is full. It can also be triggered by the opcode OP_GC (0xD4)
- Mark phase: GC traverses the stack and marks every possible alive heap Cell. - Sweep phase: GC traverses the heap and frees all dead Cells. - Compact phase: I did not implement the memory compaction because I use NextFreeCell() to prevent memory fragmentation; NextFreeCell() will find the next free cell and the new array will be built in this location. In other words, I will reuse the freed location (free list) first.
Track each array reference This mechanism will be triggered when a stack frame is destroyed.
Firstly, I built a tracking table when a new array was allocated. When a stack frame is destroyed, I check the tracking table and free all the array allocated by this frame (except the array that returns by this frame).
I implement GUI using GTK2.0 rather that Nuklear. Dependency:
sudo apt-get install gtk2.0
sudo apt-get install gimp
sudo apt install libcanberra-gtk-module libcanberra-gtk3-module
sudo apt-get install libatk-adaptor libgail-common
Usage:
make ijvm-gui
./iivm-gui
When SIGINT is captured by the signal handler, the current IJVM instance will be saved in a file called ijvm.config.
I use 2 methods to compact the saved ijvm.config file:
- Instead of saving the entire main frame local variable array which has 2^16 words, I count the variable number when loading the program and only save these variable to the ijvm.config file.
- I discard the whole original program and only keep parsed text and constant pool sections. Usage:
./ijvm -r binary
or
./ijvm -resume binary
Requires make and GCC or Clang
Run make ijvm
to build the ijvm binary
You can enable the debug print (dprintf
) found in include/util.h
by
setting the -DDEBUG
compiler flag (e.g., make clean && make testbasic CFLAGS=-DDEBUG
).
Run an IJVM program using ./ijvm binary
. For example ./ijvm files/advanced/Tanenbaum.ijvm
.
Add your header files to the folder include
.
To run a specific test run make run_testX
(e.g. make run_test1
).
make testbasic
.make testadvanced
.make testleaks
make testsanitizers
(requires LLVM)make pedantic
You can debug the tests by running the binaries generated by
make build_tests
through GDB.
Generate a gzipped tarball of your project using the make dist
command.
Make sure to double check that all your required files are included in the tarball.
You need a valid C11 compiler, such as clang or gcc, as well as glibc. Do not use any non-standard libraries.
To develop on Windows, we recommend using the Windows Subsystem for Linux,
and installing Ubuntu from the Microsoft Store. Once you have gone through the
setup steps, you can access the Linux command line by executing bash
from
the Windows command line. You can access your files in the Windows file system
through mount points (e.g. cd /mnt/c/Users/<username>/Desktop
).
Both glibc, gcc, and make are included in the package build-essential
. To
install this package, execute the following commands:
sudo apt-get update
sudo apt-get install build-essential
Now you can compile the project by navigating to this directory and executing
the make
command.
You can install the goJASM assembler by executing make tools
. This will
download a goJASM executable in the tools directory.