diff --git a/ISA.md b/ISA.md index fd21aee..0b16814 100644 --- a/ISA.md +++ b/ISA.md @@ -1,11 +1,13 @@ There is ChaiVM's accumulator(acc) 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 | | Mov | RR | i64, moves value from ri to rj | -| Ldia | R | i64, loads constant [i] to acc | +| 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 | @@ -16,7 +18,7 @@ There is ChaiVM's accumulator(acc) based ISA. | Muli | I | i64, mul acc by [imm] | | Div | R | i64, divides acc by R1 | | Divi | I | i64, divides acc by [imm] | -| Ldiaf | R | f64, loads constant [i] to acc | +| 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 | @@ -25,23 +27,23 @@ There is ChaiVM's accumulator(acc) based ISA. | 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 char, containing by the first byte of acc | -| IcScani | N | Intrinsic. Scans i64 from stdin | -| IcScanf | N | Intrinsic. Scans f64 from stdin | +| 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 | N | i64, if acc == r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_icmpne | RI | i64, if acc != r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_icmpgt | RI | i64, if acc > r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_icmpge | RI | i64, if acc >= r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_icmplt | RI | i64, if acc < r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_icmple | RI | i64, if acc <= r1 then branch to instruction at offset [imm] otherwise just next instr | -| If_acmpeq | RI | ref, if references are equal, branch to instruction at offset [imm] | -| If_acmpne | RI | ref, if references are not equal, branch to instruction at offset [imm] | +| 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 | +| 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 | | С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 | -| Goto | I | Goes to another instruction at branchoffset [imm] | +| 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. | @@ -49,10 +51,19 @@ There is ChaiVM's accumulator(acc) based ISA. | 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 | +| 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. | To generate this file use the following python script: ```shell diff --git a/include/ChaiVM/interpreter/executor.hpp b/include/ChaiVM/interpreter/executor.hpp index aec3a6c..34885e9 100644 --- a/include/ChaiVM/interpreter/executor.hpp +++ b/include/ChaiVM/interpreter/executor.hpp @@ -5,6 +5,7 @@ #include "ChaiVM/interpreter/code-manager/code-manager.hpp" #include "decoder.hpp" #include "frame.hpp" +#include "objects.hpp" namespace chai::interpreter { @@ -80,17 +81,9 @@ class Executor { void newf64array(Instruction ins); void get_f64from_arr(Instruction ins); void set_f64in_arr(Instruction ins); - - // @todo #97:90min Implement array of objects. - void new_ref_arr(Instruction ins) { - std::cout << "new_ref_arr " << ins.operation << std::endl; - } - void get_ref_from_arr(Instruction ins) { - std::cout << "get_ref_from_arr " << ins.operation << std::endl; - } - void set_ref_in_arr(Instruction ins) { - std::cout << "set_ref_in_arr " << ins.operation << std::endl; - } + void new_ref_arr(Instruction ins); + void get_ref_from_arr(Instruction ins); + void set_ref_in_arr(Instruction ins); void string_print(Instruction ins); void string_concat(Instruction ins); void string_len(Instruction ins); @@ -187,4 +180,10 @@ class InvalidInstruction : public std::runtime_error { const char *what() const noexcept override; }; +class IndexOutOfBoundary : public std::runtime_error { +public: + explicit IndexOutOfBoundary(char const *msg); + IndexOutOfBoundary(const std::string &msg); + const char *what() const noexcept override; +}; } // namespace chai::interpreter diff --git a/include/ChaiVM/interpreter/objects.hpp b/include/ChaiVM/interpreter/objects.hpp index 745f941..1e09fac 100644 --- a/include/ChaiVM/interpreter/objects.hpp +++ b/include/ChaiVM/interpreter/objects.hpp @@ -7,9 +7,22 @@ namespace chai::interpreter { -// @todo #97:90min Add allocator here. +/** + * Immidiate for klass of object array. Not elegant decision. + * @todo #100:60min Figure out how to designate such a class. + */ +const Immidiate OBJ_ARR_IMM = UINT16_MAX - 1; + struct ObjectHeader { + + /** + * Size of object. Array of objects is an object too. + */ chsize_t size_; + + /** + * Number of klass in "klass pool". + */ Immidiate klassId_; }; @@ -23,6 +36,7 @@ class Object { public: explicit Object(ObjectHeader *header, chsize_t *fields); + /** * Ctor. * Create object from ref to object. @@ -30,13 +44,35 @@ class Object { */ explicit Object(chsize_t ref); - chsize_t getField(Immidiate offset) const; + /** + * Get count of members. + */ + chsize_t countMembers() const; + + /** + * Get member. + * @param offset Offset starting from {@link members_} (i.e. without + * header). + * @return Value of member. + */ + chsize_t getMember(Immidiate offset) const; - void setField(Immidiate offset, chsize_t value) const; + /** + * Set member. + * @param offset Offset starting from {@link members_} (i.e. without + * header). + * @param value Value to be set. + */ + void setMember(Immidiate offset, chsize_t value) const; private: ObjectHeader *header_; - chsize_t *fields_; + + /** + * In case of ordinary object this is array of fields. + * In case of object array this is array of refs. + */ + chsize_t *members_; }; } // namespace chai::interpreter diff --git a/include/ChaiVM/memory/allocator.hpp b/include/ChaiVM/memory/allocator.hpp index 197ed35..032acf6 100644 --- a/include/ChaiVM/memory/allocator.hpp +++ b/include/ChaiVM/memory/allocator.hpp @@ -8,7 +8,19 @@ template class IAllocator { public: using value_type = T; IAllocator() noexcept = default; + + /** + * Allocate memory for n T-objects. + * @param n Number of objects. + * @return Ponter to start of allocated mem. + */ virtual T *allocate(size_t n) = 0; + + /** + * Deallocate memory. + * @param p Where deallocate from. + * @param n Number of objects to be deallocated. + */ virtual void deallocate(T *p, size_t n) = 0; virtual ~IAllocator() {} }; diff --git a/include/ChaiVM/memory/linear-allocator.hpp b/include/ChaiVM/memory/linear-allocator.hpp index c54f6ef..d42be3f 100644 --- a/include/ChaiVM/memory/linear-allocator.hpp +++ b/include/ChaiVM/memory/linear-allocator.hpp @@ -13,6 +13,9 @@ namespace chai::memory { template class LinearAllocator : IAllocator { public: + /** + * Do not remove. + */ using value_type = T; explicit LinearAllocator(LinearBuffer &buffer) : buffer_(buffer) {} diff --git a/include/ChaiVM/types.hpp b/include/ChaiVM/types.hpp index 330e44a..bc81af5 100644 --- a/include/ChaiVM/types.hpp +++ b/include/ChaiVM/types.hpp @@ -7,4 +7,9 @@ namespace chai { using bytecode_t = uint64_t; using chsize_t = uint64_t; +/** + * Null object ref. + */ +const chsize_t CHAI_NULL = 0; + } // namespace chai diff --git a/src/ChaiVM/interpreter/executor.cpp b/src/ChaiVM/interpreter/executor.cpp index 8c6751d..99de0b4 100644 --- a/src/ChaiVM/interpreter/executor.cpp +++ b/src/ChaiVM/interpreter/executor.cpp @@ -361,7 +361,6 @@ void Executor::get_f64from_arr(Instruction ins) { advancePc(); DO_NEXT_INS(); } - void Executor::set_f64in_arr(Instruction ins) { auto i = static_cast((*currentFrame_)[ins.r1]); auto *arr = reinterpret_cast(acc()); @@ -369,7 +368,42 @@ void Executor::set_f64in_arr(Instruction ins) { advancePc(); DO_NEXT_INS(); } - +void Executor::new_ref_arr(Instruction ins) { + chsize_t len = acc(); + memory::LinearAllocator allocator{objectsBuffer_}; + chsize_t num_bytes = sizeof(ObjectHeader) + len * sizeof(chsize_t); + auto *object_arr = new (allocator.allocate(num_bytes)) uint8_t[num_bytes](); + auto *pheader = reinterpret_cast(object_arr); + auto *members = + reinterpret_cast(object_arr + sizeof(ObjectHeader)); + pheader->size_ = num_bytes; + pheader->klassId_ = OBJ_ARR_IMM; + for (int i = 0; i < len; ++i) { + members[i] = CHAI_NULL; + } + acc() = std::bit_cast(object_arr); + advancePc(); + DO_NEXT_INS(); +} +void Executor::get_ref_from_arr(Instruction ins) { + auto i = static_cast((*currentFrame_)[ins.r1]); + Object object{acc()}; + if (i >= object.countMembers()) { + throw IndexOutOfBoundary("index " + std::to_string(i) + + " is greater than array length " + + std::to_string(object.countMembers())); + } + acc() = object.getMember(i * sizeof(chsize_t)); + advancePc(); + DO_NEXT_INS(); +} +void Executor::set_ref_in_arr(Instruction ins) { + auto i = static_cast((*currentFrame_)[ins.r1]); + chsize_t new_ref = (*currentFrame_)[ins.r2]; + Object{acc()}.setMember(i * sizeof(chsize_t), new_ref); + advancePc(); + DO_NEXT_INS(); +} void Executor::string_print(Instruction ins) { const std::string &str = codeManager_->getStringByStringPoolPos(acc()); std::cout << str << std::endl; @@ -436,14 +470,14 @@ void Executor::star_ref(Instruction ins) { void Executor::get_field(Instruction ins) { Immidiate offset = ins.immidiate; Object object{acc()}; - acc() = object.getField(offset); + acc() = object.getMember(offset); advancePc(); DO_NEXT_INS() } void Executor::set_field(Instruction ins) { Immidiate offset = ins.immidiate; Object object{acc()}; - object.setField(offset, (*currentFrame_)[ins.r1]); + object.setMember(offset, (*currentFrame_)[ins.r1]); advancePc(); DO_NEXT_INS() } @@ -455,4 +489,11 @@ const char *InvalidInstruction::what() const noexcept { return runtime_error::what(); } +IndexOutOfBoundary::IndexOutOfBoundary(const char *msg) : runtime_error(msg) {} +IndexOutOfBoundary::IndexOutOfBoundary(const std::string &msg) + : runtime_error(msg) {} +const char *IndexOutOfBoundary::what() const noexcept { + return runtime_error::what(); +} + } // namespace chai::interpreter diff --git a/src/ChaiVM/interpreter/objects.cpp b/src/ChaiVM/interpreter/objects.cpp index 0d5bf2f..81c5ff1 100644 --- a/src/ChaiVM/interpreter/objects.cpp +++ b/src/ChaiVM/interpreter/objects.cpp @@ -6,20 +6,24 @@ namespace chai::interpreter { Object::Object(ObjectHeader *header, chsize_t *fields) - : header_(header), fields_(fields) {} + : header_(header), members_(fields) {} Object::Object(chsize_t ref) : header_(std::bit_cast(ref)), - fields_(std::bit_cast(header_ + 1)) {} + members_(std::bit_cast(header_ + 1)) {} -chsize_t Object::getField(Immidiate offset) const { - assert(offset % 8 == 0); - return fields_[offset / 8]; +chsize_t Object::countMembers() const { + return (header_->size_ - sizeof(ObjectHeader)) / sizeof(chsize_t); } -void Object::setField(Immidiate offset, chsize_t value) const { - assert(offset % 8 == 0); - fields_[offset / 8] = value; +chsize_t Object::getMember(Immidiate offset) const { + assert(offset % sizeof(chsize_t) == 0); + return members_[offset / sizeof(chsize_t)]; +} + +void Object::setMember(Immidiate offset, chsize_t value) const { + assert(offset % sizeof(chsize_t) == 0); + members_[offset / sizeof(chsize_t)] = value; } } // namespace chai::interpreter diff --git a/test/ChaiVM/interpreter/executor_test.cpp b/test/ChaiVM/interpreter/executor_test.cpp index 752474c..ebd2f8d 100644 --- a/test/ChaiVM/interpreter/executor_test.cpp +++ b/test/ChaiVM/interpreter/executor_test.cpp @@ -760,7 +760,7 @@ TEST_F(ExecutorTest, SetField1) { EXPECT_EQ(Object{std::bit_cast( (char *)objectBuffer_.currentPosition() - bar_size)} - .getField(0), + .getMember(0), val); EXPECT_EQ(exec_.getCurrentFrame(), nullptr); } @@ -791,11 +791,11 @@ TEST_F(ExecutorTest, SetField2) { EXPECT_EQ( Object{std::bit_cast( (char *)objectBuffer_.currentPosition() - 2 * bar_size)} - .getField(0), + .getMember(0), 0); EXPECT_EQ(Object{std::bit_cast( (char *)objectBuffer_.currentPosition() - bar_size)} - .getField(0), + .getMember(0), val); EXPECT_EQ(exec_.getCurrentFrame(), nullptr); } @@ -829,11 +829,132 @@ TEST_F(ExecutorTest, GetField) { EXPECT_EQ( Object{std::bit_cast( (char *)objectBuffer_.currentPosition() - 2 * cat_size)} - .getField(0), + .getMember(0), 0); EXPECT_EQ(Object{std::bit_cast( (char *)objectBuffer_.currentPosition() - cat_size)} - .getField(8), + .getMember(8), val); EXPECT_EQ(exec_.getCurrentFrame(), nullptr); } + +/* + * Object arr[] = new Object[len]; + */ +TEST_F(ExecutorTest, NewRefArray) { + constexpr auto len = static_cast(50); + loadWithConst(Ldia, len); + load(); + load(); + update(); + exec_.run(); + + chai::chsize_t object_array_size = + len * sizeof(chai::chsize_t) + sizeof(ObjectHeader); + EXPECT_EQ(objectBuffer_.offset(), object_array_size); + for (int i = 0; i < len; ++i) { + EXPECT_EQ(Object{std::bit_cast( + (char *)objectBuffer_.currentPosition() - + object_array_size)} + .getMember(i * sizeof(chai::chsize_t)), + chai::CHAI_NULL); + } + auto buff_start = static_cast(objectBuffer_.currentPosition()) - + object_array_size; + EXPECT_EQ(((ObjectHeader *)buff_start)->klassId_, OBJ_ARR_IMM); + EXPECT_EQ(exec_.getCurrentFrame(), nullptr); +} + +/* + * Bar bar = new Bar(); + * bar.num = val; + * Bar arr[] = new Bar[len]; + * arr[index] = bar; + * return arr[index].num + */ +TEST_F(ExecutorTest, SetGetInObjectArray_1) { + auto bar_klass = chaiFile_.registerKlass("Bar"); + chaiFile_.addField(bar_klass, "bar.num", 0U, FieldTag::I64); + chai::chsize_t val = 125; + Immidiate imm_val = chaiFile_.addConst(std::make_unique(val)); + + constexpr auto index = static_cast(25); + loadWithConst(Ldia, index); + load(R1); + load(imm_val); + load(R3); + load(bar_klass); + load(R3, 0); + load(R2); + + int len = 200; + Immidiate imm_len = chaiFile_.addConst(std::make_unique(len)); + load(imm_len); + load(); + load(R1, R2); + load(R1); + load(0); + load(); + update(); + exec_.run(); + + EXPECT_EQ(static_cast(exec_.acc()), val); + EXPECT_EQ(exec_.getCurrentFrame(), nullptr); +} + +/* + * Bar arr1[] = new Bar[len]; + * Bar bar = new Bar(); + * bar.num = val; + * Bar arr2[] = new Bar[len]; + * arr2[index] = bar; + * return arr[index].num + */ +TEST_F(ExecutorTest, SetGetInObjectArray_2) { + auto bar_klass = chaiFile_.registerKlass("Bar"); + chaiFile_.addField(bar_klass, "bar.num", 0U, FieldTag::I64); + + int len = 42; + Immidiate imm_len = chaiFile_.addConst(std::make_unique(len)); + load(imm_len); + load(); + + chai::chsize_t val = 125; + Immidiate imm_val = chaiFile_.addConst(std::make_unique(val)); + constexpr auto index = static_cast(25); + loadWithConst(Ldia, index); + load(R1); + load(imm_val); + load(R3); + load(bar_klass); + load(R3, 0); + load(R2); + + load(imm_len); + load(); + load(R1, R2); + load(R1); + load(0); + load(); + update(); + exec_.run(); + + size_t size_of_bar = sizeof(ObjectHeader) + 1 * sizeof(chai::chsize_t); + size_t size_of_each_arr = + sizeof(ObjectHeader) + len * sizeof(chai::chsize_t); + EXPECT_EQ(objectBuffer_.offset(), size_of_bar + 2 * (size_of_each_arr)); + EXPECT_EQ(static_cast(exec_.acc()), val); + EXPECT_EQ(exec_.getCurrentFrame(), nullptr); +} + +TEST_F(ExecutorTest, GetOutOfBoundary) { + int len = 20; + Immidiate imm_len = chaiFile_.addConst(std::make_unique(len)); + load(imm_len); + load(R1); + load(); + load(R1); + load(); + update(); + EXPECT_THROW(exec_.run(), IndexOutOfBoundary); +} diff --git a/tools/isa-spec.py b/tools/isa-spec.py index c31790e..b4de180 100644 --- a/tools/isa-spec.py +++ b/tools/isa-spec.py @@ -11,6 +11,8 @@ def main() -> int: instrs = full["instructions"] tpl = """There is ChaiVM's accumulator(acc) based ISA. +Notation: `imm` is raw value, `[imm]` is value from constant pool by number imm. + | Operation | Format | Description | |:---------:|:------:|:------------| {% for n, item in enumerate(items, 1) %} diff --git a/tools/resources/instructions.yml b/tools/resources/instructions.yml index 33b5e64..33db90e 100644 --- a/tools/resources/instructions.yml +++ b/tools/resources/instructions.yml @@ -255,12 +255,12 @@ instructions: fixedvalue: 49 mnemonic: GetRefFromArr format: R - description: "Gets object from object array in acc and puts it to acc." + description: "Gets object by index in r1 from object array in acc and puts it to acc." - fixedvalue: 50 mnemonic: SetRefInArr format: RR - description: "Sets ref value to object array in acc and puts it to acc." + description: "Sets ref value in r2 by index in r1 to object array in acc and puts it to acc." - fixedvalue: 51 mnemonic: StringPrint @@ -285,7 +285,7 @@ instructions: fixedvalue: 55 mnemonic: AllocRef format: I - description: "Ref, Creates object of klass imm, leaves ref in acc" + description: "Ref, Creates object of klass [imm], leaves ref in acc" - fixedvalue: 56 mnemonic: MovRef