Skip to content

Commit

Permalink
[#95] fix(DaedalusVm): fixup the stack after function calls
Browse files Browse the repository at this point in the history
This prevents issues when functions have too many or too few items on the stack.
  • Loading branch information
lmichaelis committed Aug 10, 2024
1 parent 7121a33 commit f53a955
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 25 deletions.
17 changes: 6 additions & 11 deletions include/zenkit/DaedalusVm.hh
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ namespace zenkit {
struct DaedalusCallStackFrame {
DaedalusSymbol const* function;
std::uint32_t program_counter;
std::uint32_t stack_ptr;
std::shared_ptr<DaedalusInstance> context;
};

Expand Down Expand Up @@ -153,20 +154,14 @@ namespace zenkit {
unsafe_call(sym);

if constexpr (std::is_same_v<R, IgnoreReturnValue>) {
// clear the stack
_m_stack_ptr = 0;
if (sym->has_return()) {
// Make sure to still pop the return value off of the stack.
_m_stack_ptr--;
}

return {};
} else if constexpr (!std::is_same_v<R, void>) {
auto ret = pop_call_return_value<R>();

// clear the stack
_m_stack_ptr = 0;

return ret;
} else {
// clear the stack
_m_stack_ptr = 0;
return pop_call_return_value<R>();
}
}

Expand Down
60 changes: 46 additions & 14 deletions src/DaedalusVm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,31 @@ namespace zenkit {
~StackGuard() {
if (_m_inhibited) return;

switch (_m_type) {
StackGuard::fix(_m_machine, _m_type);
}

/// \brief Inhibits this guard.
///
/// Calling this function will cause the guard to disengage and thus not push a
/// value onto the stack.
void inhibit() {
_m_inhibited = true;
}

static void fix(DaedalusVm* vm, DaedalusDataType type) {
switch (type) {
case DaedalusDataType::FLOAT:
_m_machine->push_float(0);
vm->push_float(0);
break;
case DaedalusDataType::INT:
case DaedalusDataType::FUNCTION:
_m_machine->push_int(0);
vm->push_int(0);
break;
case DaedalusDataType::STRING:
_m_machine->push_string("");
vm->push_string("");
break;
case DaedalusDataType::INSTANCE:
_m_machine->push_instance(nullptr);
vm->push_instance(nullptr);
break;
case DaedalusDataType::VOID:
case DaedalusDataType::CLASS:
Expand All @@ -57,14 +69,6 @@ namespace zenkit {
}
}

/// \brief Inhibits this guard.
///
/// Calling this function will cause the guard to disengage and thus not push a
/// value onto the stack.
void inhibit() {
_m_inhibited = true;
}

private:
DaedalusDataType _m_type;
DaedalusVm* _m_machine;
Expand Down Expand Up @@ -449,11 +453,39 @@ namespace zenkit {
}

void DaedalusVm::push_call(DaedalusSymbol const* sym) {
_m_call_stack.push({sym, _m_pc, _m_instance});
auto var_count = this->find_parameters_for_function(sym).size();
_m_call_stack.push({sym, _m_pc, _m_stack_ptr - static_cast<uint32_t>(var_count), _m_instance});
}

void DaedalusVm::pop_call() {
auto const& call = _m_call_stack.top();

// First, try to fix up the stack.
if (!call.function->has_return()) {
// No special logic needed, there are supposed to be no more stack frames for
// this function, so just reset the stack for the caller.
_m_stack_ptr = call.stack_ptr;
} else {
auto remaining_locals = _m_stack_ptr - call.stack_ptr;
if (remaining_locals == 0) {
// The function is supposed to have a return value, but it does not seem to have one.
// To fix this, we just insert a default value (either 0, "" or NULL).
StackGuard::fix(this, call.function->rtype());
} else if (remaining_locals > 1) {
// Now we have too many items left on the stack. Remove all of them, except the topmost one,
// since that one is supposed to be the return value of the function.
DaedalusStackFrame frame = _m_stack[--_m_stack_ptr];
_m_stack_ptr = call.stack_ptr;
_m_stack[_m_stack_ptr++] = std::move(frame);
}
// else {
// We have exactly one value to be returned (as indicated by the symbol's return type).
// That means, that the compiler did not mess up the stack management, so we can just
// return that value.
// }
}

// Second, reset PC and context, then remove the call stack frame
_m_pc = call.program_counter;
_m_instance = call.context;
_m_call_stack.pop();
Expand Down

0 comments on commit f53a955

Please sign in to comment.