Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#111 initial implementation of gc #113

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ if (CHAI_BENCH)
add_subdirectory(bench)
endif ()

find_package(Python COMPONENTS Interpreter REQUIRED)
# @todo #15:90min Generate autogen/operations.hpp into build directory.
set(OPERATIONS_DIR ${CMAKE_SOURCE_DIR}/include/ChaiVM/interpreter/autogen/operations.hpp)
add_custom_target(gen_operations_header
COMMENT "Generating ${OPERATIONS_DIR}"
OUTPUT ${OPERATIONS_DIR}
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/opcode2operation-generator.py ${OPERATIONS_DIR} ${CMAKE_SOURCE_DIR}/tools/resources/instructions.yml
COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/opcode2operation-generator.py ${OPERATIONS_DIR} ${CMAKE_SOURCE_DIR}/tools/resources/instructions.yml
)

add_library(chai_include INTERFACE)
Expand Down
112 changes: 56 additions & 56 deletions ISA.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
There is ChaiVM's accumulator(acc) based ISA.
There is ChaiVM's accumulator(getAcc) based ISA.

Notation: `imm` is raw value, `[imm]` is value from constant pool by number imm.

| Operation | Format | Description |
|:---------:|:------:|:------------|
| Nop | N | Does nothing |
| Ret | N | Returns a value via acc |
| Ret | N | Returns a value via getAcc |
| Mov | RR | i64, moves value from ri to rj |
| Ldia | I | i64, loads constant [i] to acc |
| Ldra | R | i64, loads the R1 to acc |
| Star | R | i64, copies val of acc into register ri |
| Add | R | i64, Adds R1 to acc |
| Addi | I | i64, Adds [imm] to acc |
| Sub | R | i64, sub R1 from acc |
| Subi | I | i64, sub [imm] from acc |
| Mul | R | i64, mul acc by R1 |
| Muli | I | i64, mul acc by [imm] |
| Div | R | i64, divides acc by R1 |
| Divi | I | i64, divides acc by [imm] |
| Modi | I | i64, acc = acc % [imm] |
| Ldiaf | I | f64, loads constant [i] to acc |
| Addf | R | f64, Adds R1 to acc |
| Addif | I | f64, Adds [imm] to acc |
| Subf | R | f64, sub R1 from acc |
| Subif | I | i64, sub [imm] from acc |
| Mulf | R | f64, mul acc by R1 |
| Mulif | I | f64, mul acc by [imm] |
| Divf | R | f64, divides acc by R1 |
| Divif | I | f64, divides acc by [imm] |
| IcPrint | N | Intrinsic. Prints value of acc |
| IcScani | N | Intrinsic. Scans i64 from stdin into acc |
| IcScanf | N | Intrinsic. Scans f64 from stdin into acc |
| IcSqrt | N | f64, Intrinsic. Calculates sqrt of acc. |
| IcSin | N | f64, Intrinsic. Calculates sin of acc. |
| IcCos | N | f64, Intrinsic. Calculates cos of acc. |
| If_icmpeq | RI | i64, if acc == r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpne | RI | i64, if acc != r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpgt | RI | i64, if acc > r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpge | RI | i64, if acc >= r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmplt | RI | i64, if acc < r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmple | RI | i64, if acc <= r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| Ldia | I | i64, loads constant [i] to getAcc |
| Ldra | R | i64, loads the R1 to getAcc |
| Star | R | i64, copies val of getAcc into register ri |
| Add | R | i64, Adds R1 to getAcc |
| Addi | I | i64, Adds [imm] to getAcc |
| Sub | R | i64, sub R1 from getAcc |
| Subi | I | i64, sub [imm] from getAcc |
| Mul | R | i64, mul getAcc by R1 |
| Muli | I | i64, mul getAcc by [imm] |
| Div | R | i64, divides getAcc by R1 |
| Divi | I | i64, divides getAcc by [imm] |
| Modi | I | i64, getAcc = getAcc % [imm] |
| Ldiaf | I | f64, loads constant [i] to getAcc |
| Addf | R | f64, Adds R1 to getAcc |
| Addif | I | f64, Adds [imm] to getAcc |
| Subf | R | f64, sub R1 from getAcc |
| Subif | I | i64, sub [imm] from getAcc |
| Mulf | R | f64, mul getAcc by R1 |
| Mulif | I | f64, mul getAcc by [imm] |
| Divf | R | f64, divides getAcc by R1 |
| Divif | I | f64, divides getAcc by [imm] |
| IcPrint | N | Intrinsic. Prints value of getAcc |
| IcScani | N | Intrinsic. Scans i64 from stdin into getAcc |
| IcScanf | N | Intrinsic. Scans f64 from stdin into getAcc |
| IcSqrt | N | f64, Intrinsic. Calculates sqrt of getAcc. |
| IcSin | N | f64, Intrinsic. Calculates sin of getAcc. |
| IcCos | N | f64, Intrinsic. Calculates cos of getAcc. |
| If_icmpeq | RI | i64, if getAcc == r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpne | RI | i64, if getAcc != r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpgt | RI | i64, if getAcc > r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmpge | RI | i64, if getAcc >= r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmplt | RI | i64, if getAcc < r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_icmple | RI | i64, if getAcc <= r1 then branch to instruction at offset(instructions) imm otherwise just next instr |
| If_acmpeq | RI | ref, if references are equal, branch to instruction at offset(instructions) imm |
| If_acmpne | RI | ref, if references are not equal, branch to instruction at offset(instructions) imm |
| If_null | I | ref, if reference in acc is null, branch to instruction at offset(instructions) imm |
| Сmpgf | R | f64 -> i64, compare acc with r1. Acc became 1 i64 if greater than r1, 0 if equal, otherwise -1 |
| Cmplf | R | f64 -> i64, compare acc with r1. Acc became 1 i64 if less than r1, 0 if equal, otherwise -1 |
| If_null | I | ref, if reference in getAcc is null, branch to instruction at offset(instructions) imm |
| Сmpgf | R | f64 -> i64, compare getAcc with r1. Acc became 1 i64 if greater than r1, 0 if equal, otherwise -1 |
| Cmplf | R | f64 -> i64, compare getAcc with r1. Acc became 1 i64 if less than r1, 0 if equal, otherwise -1 |
| Goto | I | Goes to another instruction at branchoffset(instructions) imm |
| Call | I | Calls function [imm]. Imm is reference to function in constant pool named constant_func_name_and_type. |
| NewI64Array | N | Allocates array of type i64 with number of elements from acc register. A reference to this new array is stored in acc. |
| GetI64FromArr | R | Gets i64 from i64 array in acc and puts it to acc. |
| SetI64InArr | RR | Sets i64 value to i64 array in acc and puts it to acc. |
| NewF64Array | N | Allocates array of type f64 with number of elements from acc register. A reference to this new array is stored in acc. |
| GetF64FromArr | R | Gets f64 from f64 array in acc and puts it to acc. |
| SetF64InArr | RR | Sets f64 value to f64 array in acc and puts it to acc. |
| NewRefArray | N | Allocates array of objects with number of elements from acc register. A reference to this new array is stored in acc. Value of every object is null. |
| GetRefFromArr | R | Gets object by index in r1 from object array in acc and puts it to acc. |
| SetRefInArr | RR | Sets ref value in r2 by index in r1 to object array in acc and puts it to acc. |
| StringPrint | N | Prints String in acc |
| StringConcat | R | Create new String, concatenating String in acc and String in r1. Result in acc. |
| StringLen | N | Puts len of String to acc |
| StringSlice | RR | Create new String slicing String placed in acc. r1 is start and r2 is finish of slicing |
| AllocRef | I | Ref, Creates object of klass [imm], leaves ref in acc |
| NewI64Array | N | Allocates array of type i64 with number of elements from getAcc register. A reference to this new array is stored in getAcc. |
| GetI64FromArr | R | Gets i64 from i64 array in getAcc and puts it to acc. |
| SetI64InArr | RR | Sets i64 value to i64 array in acc and puts it to getAcc. |
| NewF64Array | N | Allocates array of type f64 with number of elements from getAcc register. A reference to this new array is stored in getAcc. |
| GetF64FromArr | R | Gets f64 from f64 array in getAcc and puts it to acc. |
| SetF64InArr | RR | Sets f64 value to f64 array in getAcc and puts it to acc. |
| NewRefArray | N | Allocates array of objects with number of elements from getAcc register. A reference to this new array is stored in getAcc. Value of every object is null. |
| GetRefFromArr | R | Gets object by index in r1 from object array in getAcc and puts it to acc. |
| SetRefInArr | RR | Sets ref value in r2 by index in r1 to object array in getAcc and puts it to acc. |
| StringPrint | N | Prints String in getAcc |
| StringConcat | R | Create new String, concatenating String in getAcc and String in r1. Result in getAcc. |
| StringLen | N | Puts len of String to getAcc |
| StringSlice | RR | Create new String slicing String placed in getAcc. r1 is start and r2 is finish of slicing |
| AllocRef | I | Ref, Creates object of klass [imm], leaves ref in getAcc |
| MovRef | RR | Ref, copies ref from ri to rj |
| LdraRef | R | Ref, copies ref of the R1 to acc |
| StarRef | R | Ref, copies ref of acc into register ri |
| GetField | I | Ref, load value of field to acc. imm is offset of field (starting from all fields) Value can be ref, array or String |
| SetField | RI | Ref, set value of register to field imm of object in acc. Value should be consistent to field signature. There are no checks in runtime. |
| LdraRef | R | Ref, copies ref of the R1 to getAcc |
| StarRef | R | Ref, copies ref of getAcc into register ri |
| GetField | I | Ref, load value of field to getAcc. imm is offset of field (starting from all fields) Value can be ref, array or String |
| SetField | RI | Ref, set value of register to field imm of object in getAcc. Value should be consistent to field signature. There are no checks in runtime. |

To generate this file use the following python script:
```shell
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ format:
./tools/clang-format.sh $(PWD)/$(BENCH_DIR)
.PHONY: build
build: init
cmake -S $(PWD) -B $(PWD)/$(BUILD_DIR) -DCHAIVM_ADD_MEM_SANITIZER=OFF -DCHAIVM_ADD_THREAD_SANITIZER=OFF
cmake -S $(PWD) -B $(PWD)/$(BUILD_DIR) -DCHAIVM_ADD_MEM_SANITIZER=OFF -DCHAIVM_ADD_THREAD_SANITIZER=OFF -DCMAKE_BUILD_TYPE=RELEASE
cmake --build $(PWD)/$(BUILD_DIR) --parallel $(JOBS)

.PHONY: test
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main 8 0 {
Star r3
Ldra r3
Mul r2
Printc acc
Printc getAcc
Mov 0 r0
Ret
}
Expand Down
5 changes: 5 additions & 0 deletions include/ChaiVM/interpreter/code-manager/klass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ struct Klass {

chsize_t nFields() const;
chsize_t instanceSize() const;

inline bool fieldIsObject(chsize_t offset) const {
assert(offset % sizeof(chsize_t) == 0);
return fields_[offset / sizeof(chsize_t)].isObject_;
}
};

} // namespace chai::interpreter
16 changes: 15 additions & 1 deletion include/ChaiVM/interpreter/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "ChaiVM/memory/traced-allocator.hpp"
#include "decoder.hpp"
#include "frame.hpp"
#include "garbage-collector.hpp"
#include "objects.hpp"

namespace chai::interpreter {
Expand All @@ -26,9 +27,18 @@ class Executor {
void run();

chsize_t &acc();
chsize_t acc() const;
chsize_t getAcc() const;

/**
* Checks if reference is stored in accumulator.
* @return true if accumulator stores reference, false otherwise.
*/
bool isAccRef() const;

Frame const *getCurrentFrame() const;
const CodeManager* getCodeManager() const;
memory::TracedByteAllocator& getObjectAllocator();
const GarbageCollector& getGC() const;

private:
chsize_t &pc();
Expand Down Expand Up @@ -167,13 +177,17 @@ class Executor {
&Executor::set_field,
};

void triggerGC();

private:
chsize_t acc_;
bool isAccRef_ = false;
CodeManager *codeManager_;
memory::LinearBuffer &framesBuffer_;
memory::LinearBuffer &primitivesBuffer_;
memory::TracedByteAllocator &objectsAllocator_;
Frame *currentFrame_ = nullptr;
GarbageCollector gc_{*this};
};

inline void Executor::advancePc() { pc() += sizeof(bytecode_t); }
Expand Down
10 changes: 9 additions & 1 deletion include/ChaiVM/interpreter/frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "ChaiVM/interpreter/code-manager/func-struct.hpp"
#include "ChaiVM/memory/linear-allocator.hpp"
#include "ChaiVM/memory/linear-buffer.hpp"
#include <bitset>

namespace chai::interpreter {

Expand All @@ -15,14 +16,19 @@ class Frame {

chai::chsize_t &operator[](size_t n) &;
const chsize_t &operator[](size_t n) const &;
size_t size() const;

bool isRegisterReference(RegisterId reg_id) const;
void setRegisterIsRef(RegisterId reg_id, bool val);

/**
* Get state.
* @return state.
*/
std::vector<chsize_t> copyState();
std::vector<chsize_t> copyState() const;

Frame *back();
const Frame *back() const;

public:
Function const &func_;
Expand All @@ -32,6 +38,8 @@ class Frame {
Frame *prev_;
size_t regsize_;
std::vector<chsize_t, memory::LinearAllocator<chsize_t>> registers_;
// @todo #111:60min use custom allocator here
std::bitset<256> isRegRef_;
};

} // namespace chai::interpreter
33 changes: 33 additions & 0 deletions include/ChaiVM/interpreter/garbage-collector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "ChaiVM/interpreter/objects.hpp"
#include <unordered_set>

// forward declaration for executor due to cyclic dependency
namespace chai::interpreter {
class Executor;
}

namespace chai::interpreter {

using chai::interpreter::Executor;

class GarbageCollector {
public:
GarbageCollector(Executor &exec) : exec_(exec) {}

void collect();
const std::vector<interpreter::Object> &getRoots() const;
private:
void collectRoots();
void mark();
void markObjects(Object obj);
void markObjectArrays(Object obj);
void sweep();

std::vector<interpreter::Object> roots_{};
std::unordered_set<chsize_t> markedRefs_{};
Executor &exec_;
};

} // namespace chai::interpreter
18 changes: 17 additions & 1 deletion include/ChaiVM/interpreter/objects.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include "ChaiVM/interpreter/code-manager/klass.hpp"
#include "ChaiVM/memory/linear-allocator.hpp"
#include "ChaiVM/memory/linear-buffer.hpp"
#include "executor.hpp"
#include <cassert>

namespace chai::interpreter {
Expand All @@ -25,6 +24,11 @@ struct ObjectHeader {
* Number of klass in "klass pool".
*/
Immidiate klassId_;

/**
* Flag that object still has references
*/
bool isMarked_;
};

/**
Expand Down Expand Up @@ -66,6 +70,11 @@ class Object {
*/
void setMember(Immidiate offset, chsize_t value) const;

bool isMarked() const;
void mark();
void unmark();
Immidiate klassId() const;

protected:
ObjectHeader *header_;

Expand Down Expand Up @@ -97,6 +106,13 @@ class ObjectArray final : private Object {
*/
explicit ObjectArray(chsize_t ref);

/**
* Ctor.
* Create object array from object.
* @param obj The object
*/
explicit ObjectArray(Object obj);

chsize_t length() const;

chai::chsize_t &operator[](int64_t i) &;
Expand Down
22 changes: 17 additions & 5 deletions include/ChaiVM/memory/traced-allocator.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
#pragma once

#include <list>
#include <exception>

#include "ChaiVM/memory/free-list-trash/free-list-allocator.hpp"
#include "ChaiVM/types.hpp"

namespace chai::memory {

class out_of_memory_exception final : public std::bad_alloc {
public:
const char* what() const noexcept {
return msg;
}
private:
static constexpr const char* msg = "VM is out of memory";
};

struct AllocationInfo final {
void *ptr = nullptr;
size_t size = 0;
Expand All @@ -16,7 +26,7 @@ struct AllocationInfo final {
class TracedByteAllocator final {
public:
TracedByteAllocator(size_t size)
: allocator_(size, FreeListAllocator::PlacementPolicy::FIND_FIRST) {
: size_(size), allocator_(size, FreeListAllocator::PlacementPolicy::FIND_FIRST) {
allocator_.Init();
}

Expand All @@ -27,7 +37,7 @@ class TracedByteAllocator final {
allocations_.push_back(AllocationInfo{out, bytes, false});
allocated_ += bytes;
} else {
throw std::bad_alloc();
throw out_of_memory_exception();
}

return out;
Expand All @@ -38,11 +48,13 @@ class TracedByteAllocator final {
// allocations_ are changed by GC
}

auto &allocated() noexcept { return allocated_; }
auto const &allocated() const noexcept { return allocated_; }
auto &allocations() noexcept { return allocations_; }
size_t &allocated() { return allocated_; }
size_t const &allocated() const noexcept { return allocated_; }
auto &allocations() { return allocations_; }
size_t size() { return size_; }

private:
size_t size_ = 0;
size_t allocated_ = 0;
std::list<AllocationInfo> allocations_;
FreeListAllocator allocator_;
Expand Down
Loading
Loading