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

Ikarus loop support #74

Merged
merged 9 commits into from
Jul 30, 2023
Merged

Ikarus loop support #74

merged 9 commits into from
Jul 30, 2023

Conversation

Try
Copy link
Contributor

@Try Try commented Jul 25, 2023

Concept:

  • CONTINUE, BREAK and END are tracked in VM (guarded by flag), assuming that it's more efficient than virtual functions based approach.
  • Added unsafe_jump function and naked calls to assist with repeat function
  • Added naked_call to assist with repeat function

@lmichaelis lmichaelis self-requested a review July 26, 2023 15:06
@lmichaelis lmichaelis added the enhancement New feature or request label Jul 26, 2023
@lmichaelis
Copy link
Member

Thanks! Hm I can't see an easy way to fix this right now but I don't like that mod-specific things are hardcoded into the base VM implementation. I'll do some tests to verify everything works correctly and then we can merge it but expect some changes to the VM later so that things like this can be achieved in a more generic way.

@Try
Copy link
Contributor Author

Try commented Jul 27, 2023

I don't like that mod-specific things are hardcoded into the base VM implementation.

I understand, also we can discuss it in this PR. In principle game needs to have a "trap" callback on some of pushv:

case opcode::pushv:
	sym = find_symbol_by_index(instr.symbol);
	if (sym == nullptr) {
		throw vm_exception {"pushv: no symbol found for index"};
	}
	if ((_m_loop_end_sym == sym || _m_loop_break_sym==sym || _m_loop_continue_sym==sym) && _m_loop_trap) {
		_m_loop_trap(*sym);
	} else {
		push_reference(sym, 0);
	}
	break;

Ideas so far:

  1. Preprocess script byte-code on game side. Not great, as game should not share such responsibility with VM; also doesn't work atm: buffer with script is immutable.
  2. Annotate phoenix::symbol with special symbol_flag::access_trap - haven't tested, might be cleaner then current solution

@lmichaelis
Copy link
Member

Preprocess script byte-code on game side. Not great, as game should not share such responsibility with VM; also doesn't work atm: buffer with script is immutable.

Yeah I think that's not the way to go about it. It would require a lot of new code and inserting instruction (for example) wouldn't really work easily since jumps reference a static offset from the beginning of the binary so we'd have to re-assign each jump address if an instruction is added which also includes function calls :/

Annotate phoenix::symbol with special symbol_flag::access_trap - haven't tested, might be cleaner then current solution

I do like the idea here. Maybe it could work like a debugger watch where you set a callback which is invoked whenever the symbol is read from or written to. This would be relatively simple and you could still call functions in the VM from within the callback.

This would probably still require naked call support which I'm totally happy with adding. Also this could be really nice for debugging scripts.

Comment on lines 553 to 555
if constexpr (sizeof...(P) > 0 && !std::is_same_v<naked_call, R>) {
check_external_params<0, P...>(params);
}
Copy link
Member

Choose a reason for hiding this comment

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

Do I understand correctly that you never want to take any parameters or return any values to the VM when using a naked call? In that case, this I feel like this should be a separate function since it uses basically none of the logic of override_function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typically naked-calls are special case, so explicit parameter pooling is good match for feature; yet must admit that for loop it's not mandatory

Copy link
Member

Choose a reason for hiding this comment

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

Okay so then let's split that logic out into a override_function_naked routine like this:

void override_function_naked(std::string_view name, std::function<void(vm&)> callback) {
    auto* sym = find_symbol_by_name(name);
    if (sym == nullptr)
        throw vm_exception {"symbol not found"};
    if (sym->is_external())
        throw vm_exception {"symbol is already an external"};

    _m_function_overrides[sym->address()] = [callback, sym](vm& machine) {
        callback(machine);
    };
}

That makes this considerably easier to read I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've set for solution in-between;
add new override for override_function(..., std::function<naked_call(vm&)) - so code can be decoupled.
decide not to introduce new name, as on user end phoenix::naked_call is already a strong hint.

@Try Try requested a review from lmichaelis July 30, 2023 14:15
Copy link
Member

@lmichaelis lmichaelis left a comment

Choose a reason for hiding this comment

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

Cool, looks good to me :)

@lmichaelis lmichaelis merged commit e481bca into GothicKit:main Jul 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants