Skip to content

Commit

Permalink
refactor(vm, compiler): adding helper methods and functions to help i…
Browse files Browse the repository at this point in the history
…ntroducing super instructions

Also placed the primary argument of super instructions at the end so that we just have to bit mask on arg
  • Loading branch information
SuperFola committed Oct 12, 2024
1 parent 5c64a9f commit e509efd
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 93 deletions.
6 changes: 3 additions & 3 deletions include/Ark/Compiler/Word.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ namespace Ark::internal
Word(const uint8_t inst, const uint16_t primary_arg, const uint16_t secondary_arg) :
opcode(inst)
{
byte_1 = static_cast<uint8_t>((primary_arg & 0xff0) >> 4);
byte_2 = static_cast<uint8_t>((primary_arg & 0x00f) << 4 | (secondary_arg & 0xf00) >> 8);
byte_3 = static_cast<uint8_t>(secondary_arg & 0x0ff);
byte_1 = static_cast<uint8_t>((secondary_arg & 0xff0) >> 4);
byte_2 = static_cast<uint8_t>((secondary_arg & 0x00f) << 4 | (primary_arg & 0xf00) >> 8);
byte_3 = static_cast<uint8_t>(primary_arg & 0x0ff);
}
};
}
Expand Down
9 changes: 9 additions & 0 deletions include/Ark/VM/VM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ namespace Ark
*/
void init() noexcept;

// ================================================
// instruction helpers
// ================================================

inline Value* loadSymbol(uint16_t id, internal::ExecutionContext& context);
inline Value* loadConstAsPtr(uint16_t id) const;
inline void store(uint16_t id, const Value* val, internal::ExecutionContext& context);
inline void setVal(uint16_t id, const Value* val, internal::ExecutionContext& context);

// ================================================
// stack related
// ================================================
Expand Down
52 changes: 52 additions & 0 deletions include/Ark/VM/VM.inl
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,58 @@ inline Value VM::resolve(internal::ExecutionContext* context, std::vector<Value>
return *popAndResolveAsPtr(*context);
}

#pragma region "instruction helpers"

inline Value* VM::loadSymbol(const uint16_t id, internal::ExecutionContext& context)
{
context.last_symbol = id;
if (Value* var = findNearestVariable(context.last_symbol, context); var != nullptr) [[likely]]
{
// push internal reference, shouldn't break anything so far, unless it's already a ref
if (var->valueType() == ValueType::Reference)
return var->reference();
return var;
}
else [[unlikely]]
throwVMError(internal::ErrorKind::Scope, fmt::format("Unbound variable `{}'", m_state.m_symbols[context.last_symbol]));
return nullptr;
}

inline Value* VM::loadConstAsPtr(const uint16_t id) const
{
return &m_state.m_constants[id];
}

inline void VM::store(const uint16_t id, const Value* val, internal::ExecutionContext& context)
{
// avoid adding the pair (id, _) multiple times, with different values
Value* local = context.locals.back()[id];
if (local == nullptr) [[likely]]
context.locals.back().push_back(id, *val);
else
*local = *val;
}

inline void VM::setVal(const uint16_t id, const Value* val, internal::ExecutionContext& context)
{
if (Value* var = findNearestVariable(id, context); var != nullptr) [[likely]]
{
if (var->valueType() == ValueType::Reference)
*var->reference() = *val;
else [[likely]]
*var = *val;
}
else
throwVMError(
internal::ErrorKind::Scope,
fmt::format(
"Unbound variable `{}', can not change its value to {}",
m_state.m_symbols[id],
val->toString(*this)));
}

#pragma endregion

#pragma region "stack management"

inline Value* VM::pop(internal::ExecutionContext& context)
Expand Down
145 changes: 60 additions & 85 deletions src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,60 @@ namespace Ark
{
using namespace internal;

namespace helper
{
inline Value tail(Value* a)
{
if (a->valueType() == ValueType::List)
{
if (a->constList().size() < 2)
return Value(ValueType::List);

std::vector<Value> tmp(a->constList().size() - 1);
for (std::size_t i = 1, end = a->constList().size(); i < end; ++i)
tmp[i - 1] = a->constList()[i];
return Value(std::move(tmp));
}
if (a->valueType() == ValueType::String)
{
if (a->string().size() < 2)
return Value(ValueType::String);

Value b { *a };
b.stringRef().erase(b.stringRef().begin());
return b;
}

types::generateError(
"tail",
{ { types::Contract { { types::Typedef("value", ValueType::List) } },
types::Contract { { types::Typedef("value", ValueType::String) } } } },
{ *a });
}

inline Value head(Value* a)
{
if (a->valueType() == ValueType::List)
{
if (a->constList().empty())
return Builtins::nil;
return a->constList()[0];
}
if (a->valueType() == ValueType::String)
{
if (a->string().empty())
return Value(ValueType::String);
return Value(std::string(1, a->stringRef()[0]));
}

types::generateError(
"head",
{ { types::Contract { { types::Typedef("value", ValueType::List) } },
types::Contract { { types::Typedef("value", ValueType::String) } } } },
{ *a });
}
}

VM::VM(State& state) noexcept :
m_state(state), m_exit_code(0), m_running(false)
{
Expand Down Expand Up @@ -370,23 +424,13 @@ namespace Ark

TARGET(LOAD_SYMBOL)
{
context.last_symbol = arg;
if (Value* var = findNearestVariable(context.last_symbol, context); var != nullptr) [[likely]]
{
// push internal reference, shouldn't break anything so far, unless it's already a ref
if (var->valueType() == ValueType::Reference)
push(var->reference(), context);
else
push(var, context);
}
else [[unlikely]]
throwVMError(ErrorKind::Scope, fmt::format("Unbound variable `{}'", m_state.m_symbols[context.last_symbol]));
push(loadSymbol(arg, context), context);
DISPATCH();
}

TARGET(LOAD_CONST)
{
push(&(m_state.m_constants[arg]), context);
push(loadConstAsPtr(arg), context);
DISPATCH();
}

Expand All @@ -399,33 +443,13 @@ namespace Ark

TARGET(STORE)
{
{
Value val = *popAndResolveAsPtr(context);
// avoid adding the pair (id, _) multiple times, with different values
Value* local = context.locals.back()[arg];
if (local == nullptr) [[likely]]
context.locals.back().push_back(arg, val);
else
*local = val;
}

store(arg, popAndResolveAsPtr(context), context);
DISPATCH();
}

TARGET(SET_VAL)
{
{
Value val = *popAndResolveAsPtr(context);
if (Value* var = findNearestVariable(arg, context); var != nullptr) [[likely]]
{
if (var->valueType() == ValueType::Reference)
*var->reference() = val;
else [[likely]]
*var = val;
}
else
throwVMError(ErrorKind::Scope, fmt::format("Unbound variable `{}', can not change its value to {}", m_state.m_symbols[arg], val.toString(*this)));
}
setVal(arg, popAndResolveAsPtr(context), context);
DISPATCH();
}

Expand Down Expand Up @@ -894,63 +918,14 @@ namespace Ark
TARGET(TAIL)
{
Value* a = popAndResolveAsPtr(context);

if (a->valueType() == ValueType::List)
{
if (a->constList().size() < 2)
push(Value(ValueType::List), context);
else
{
std::vector<Value> tmp(a->constList().size() - 1);
for (std::size_t i = 1, end = a->constList().size(); i < end; ++i)
tmp[i - 1] = a->constList()[i];
push(Value(std::move(tmp)), context);
}
}
else if (a->valueType() == ValueType::String)
{
if (a->string().size() < 2)
push(Value(ValueType::String), context);
else
{
Value b { *a };
b.stringRef().erase(b.stringRef().begin());
push(std::move(b), context);
}
}
else
types::generateError(
"tail",
{ { types::Contract { { types::Typedef("value", ValueType::List) } },
types::Contract { { types::Typedef("value", ValueType::String) } } } },
{ *a });
push(helper::tail(a), context);
DISPATCH();
}

TARGET(HEAD)
{
Value* a = popAndResolveAsPtr(context);

if (a->valueType() == ValueType::List)
{
if (a->constList().empty())
push(Builtins::nil, context);
else
push(a->constList()[0], context);
}
else if (a->valueType() == ValueType::String)
{
if (a->string().empty())
push(Value(ValueType::String), context);
else
push(Value(std::string(1, a->stringRef()[0])), context);
}
else
types::generateError(
"head",
{ { types::Contract { { types::Typedef("value", ValueType::List) } },
types::Contract { { types::Typedef("value", ValueType::String) } } } },
{ *a });
push(helper::head(a), context);
DISPATCH();
}

Expand Down
20 changes: 15 additions & 5 deletions tests/unittests/CompilerSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@ ut::suite<"Compiler"> compiler_suite = [] {
expect(that % word.byte_3 == 0x78);
};

should("split arguments evenly between 3 bytes") = [] {
const auto word = Ark::internal::Word(12, 0x0567, 0x089a);
constexpr uint16_t primary_arg = 0x0567;
constexpr uint16_t secondary_arg = 0x089a;
const auto word = Ark::internal::Word(12, primary_arg, secondary_arg);
should("split arguments evenly between 3 bytes") = [&] {
expect(that % word.opcode == 12);
expect(that % word.byte_1 == 0x56);
expect(that % word.byte_2 == 0x78);
expect(that % word.byte_3 == 0x9a);
expect(that % word.byte_1 == 0x89);
expect(that % word.byte_2 == 0xa5);
expect(that % word.byte_3 == 0x67);
};

should("be able to unpack both arguments from word") = [&] {
const uint8_t padding = word.byte_1;
const auto arg = static_cast<uint16_t>((word.byte_2 << 8) | word.byte_3);

expect(that % primary_arg == (arg & 0x0fff));
expect(that % secondary_arg == ((padding << 4) | (arg & 0xf000) >> 12));
};
};
};

0 comments on commit e509efd

Please sign in to comment.