Skip to content

Commit

Permalink
#100: Implemented object arrays (#103)
Browse files Browse the repository at this point in the history
* #100: Implemented object arrays

* #100: Added IndexOutOfBoundary exception

* #100: Updated ISA md

* #100: Updated ISA md

* #100: Added docks

* #100: using value_type = T;

* #100: fix

* #100: make format

* #100: chai::CHAI_NULL
  • Loading branch information
levBagryansky authored Jun 13, 2024
1 parent ece1f4d commit 15c4a03
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 49 deletions.
39 changes: 25 additions & 14 deletions ISA.md
Original file line number Diff line number Diff line change
@@ -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 |
Expand All @@ -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 |
Expand All @@ -25,34 +27,43 @@ 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. |
| 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 |
| 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
Expand Down
21 changes: 10 additions & 11 deletions include/ChaiVM/interpreter/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ChaiVM/interpreter/code-manager/code-manager.hpp"
#include "decoder.hpp"
#include "frame.hpp"
#include "objects.hpp"

namespace chai::interpreter {

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
44 changes: 40 additions & 4 deletions include/ChaiVM/interpreter/objects.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};

Expand All @@ -23,20 +36,43 @@ class Object {

public:
explicit Object(ObjectHeader *header, chsize_t *fields);

/**
* Ctor.
* Create object from ref to object.
* @param ref Ref to object (usually contains in register).
*/
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
12 changes: 12 additions & 0 deletions include/ChaiVM/memory/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ template <class T> 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() {}
};
Expand Down
3 changes: 3 additions & 0 deletions include/ChaiVM/memory/linear-allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ namespace chai::memory {

template <class T> class LinearAllocator : IAllocator<T> {
public:
/**
* Do not remove.
*/
using value_type = T;

explicit LinearAllocator(LinearBuffer &buffer) : buffer_(buffer) {}
Expand Down
5 changes: 5 additions & 0 deletions include/ChaiVM/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
49 changes: 45 additions & 4 deletions src/ChaiVM/interpreter/executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,49 @@ void Executor::get_f64from_arr(Instruction ins) {
advancePc();
DO_NEXT_INS();
}

void Executor::set_f64in_arr(Instruction ins) {
auto i = static_cast<int64_t>((*currentFrame_)[ins.r1]);
auto *arr = reinterpret_cast<double *>(acc());
arr[i] = std::bit_cast<double>((*currentFrame_)[ins.r2]);
advancePc();
DO_NEXT_INS();
}

void Executor::new_ref_arr(Instruction ins) {
chsize_t len = acc();
memory::LinearAllocator<uint8_t> 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<ObjectHeader *>(object_arr);
auto *members =
reinterpret_cast<chsize_t *>(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<chsize_t>(object_arr);
advancePc();
DO_NEXT_INS();
}
void Executor::get_ref_from_arr(Instruction ins) {
auto i = static_cast<int64_t>((*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<int64_t>((*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;
Expand Down Expand Up @@ -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()
}
Expand All @@ -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
20 changes: 12 additions & 8 deletions src/ChaiVM/interpreter/objects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ObjectHeader *>(ref)),
fields_(std::bit_cast<chsize_t *>(header_ + 1)) {}
members_(std::bit_cast<chsize_t *>(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
Loading

3 comments on commit 15c4a03

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 15c4a03 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 97-491954b0 disappeared from include/ChaiVM/interpreter/executor.hpp), that's why I closed #100. Please, remember that the puzzle was not necessarily removed in this particular commit. Maybe it happened earlier, but we discovered this fact only now.

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 15c4a03 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 97-0dcc99de disappeared from include/ChaiVM/interpreter/objects.hpp), that's why I closed #101. Please, remember that the puzzle was not necessarily removed in this particular commit. Maybe it happened earlier, but we discovered this fact only now.

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 15c4a03 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 100-5c7c5a8c discovered in include/ChaiVM/interpreter/objects.hpp) and submitted as #108. Please, remember that the puzzle was not necessarily added in this particular commit. Maybe it was added earlier, but we discovered it only now.

Please sign in to comment.