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

transient and opaque instances #79

Merged
merged 8 commits into from
Aug 19, 2023
Merged

Conversation

Try
Copy link
Contributor

@Try Try commented Jul 31, 2023

This PR introduces 2 new special types for instances:

  1. opaque_instance - almost same as you propose in ticket, intended as replacement for C_SVM, C_FightAi
  2. transient_instance - callback-based workflow, that delegates read/write operations to the engine.

When working on Ikarus support, there are several use-cases:

  • Simple opaque_instance, like C_SVM - works straight
  • Simple transient_instance - allocated via mem32 framework, works straight
  • Hybrids, CoM sometimes instantiates C_ITEM for speculative purposes (get description). This creates scenario when some C_ITEM instances are 'normal' instances and some - transient. Should work, as mapping done at symbol::get_*/symbol::set_ level.
  • Mapping; when simple instance created in normal flow suppose to be mapped into mem32. OpenGothic can intercept take-pointer operator( _@ ) and create projection as transient_instance

@Try Try mentioned this pull request Aug 4, 2023
7 tasks
@Try Try changed the title mem-traps initial transient and opaque instances Aug 10, 2023
@Try Try marked this pull request as ready for review August 10, 2023 20:22
@Try
Copy link
Contributor Author

Try commented Aug 14, 2023

@lmichaelis kindly poking for code-review :)

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.

I'm not sure about embedding checks for specific instance types directly into the VM, even if they are technically VM internals. Specifically with the transient_instance, embedding these tests will prevent additional type checks on instances which may be unsafe.

Even though it might introduce a small performance degradation, I think we should instead opt for a more uniform virtual API through which instance members are accessed. Why did you choose to implement it this way?

Comment on lines 339 to 352
[[nodiscard]] PHOENIX_API std::int32_t
get_int(std::shared_ptr<instance>& context,
std::variant<int32_t, float, symbol*, std::shared_ptr<instance>>& value,
uint16_t index);
[[nodiscard]] PHOENIX_API float
get_float(std::shared_ptr<instance>& context,
std::variant<int32_t, float, symbol*, std::shared_ptr<instance>>& value,
uint16_t index);

PHOENIX_API void set_int(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, std::int32_t value);
PHOENIX_API void set_float(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, float value);
PHOENIX_API void
set_string(std::shared_ptr<instance>& context, symbol* ref, uint16_t index, std::string_view value);

Copy link
Member

Choose a reason for hiding this comment

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

I'm probably missing something here: Why are we moving the symbol accesses into a separate function? To me it looks like nothing has changed about the actual logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was introduces, when solution was callback based, yet decide to keep it - this helps avoids code duplication.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, but they should probably be private or protected then, I think, since they're basically an implementation detail.

@Try
Copy link
Contributor Author

Try commented Aug 14, 2023

Even though it might introduce a small performance degradation, I think we should instead opt for a more uniform virtual API through which instance members are accessed. Why did you choose to implement it this way?

In principle, it would be nice to have entire memory access into single function, in for example const T* get_member_ptr(...).
Unfortunately can't do it for transient_instance as there is no memory backing - just keep to in symbol.

Speaking of virtual functions: can you clarify on how interface can look like?

@lmichaelis
Copy link
Member

lmichaelis commented Aug 14, 2023

Speaking of virtual functions: can you clarify on how interface can look like?

Maybe something like:

class base_instance {
protected:
    virtual int* int_storage(size_t offset) = 0;
    virtual float* float_storage(size_t offset) = 0;
    virtual std::string* string_storage(size_t offset) = 0;

    friend class vm;
};

class instance : protected base_instance { /*...*/ };

Which is then implemented as a reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + offset) for normal instances, a reinterpret_cast<T*>(_m_storage.get() + offset) for opaque instances and implemented by an external user for transient instances. get_member_ptr can then have a template specialization for each case (or just inline each case into their respective get_-functions).

You need to differentiate between read an write they you could also use write_int and read_int and so on.

I might be missing something here but since we only access symbol values through symbol::{get_, set_}* this should work just fine.

@Try
Copy link
Contributor Author

Try commented Aug 14, 2023

While evaluating virtual-base approach:
Put 3x-get and 3x-set functions in base class only for the sake of transient_instance is overkill, I think.
get_member_ptr works just fine for normal classes as well as opaque ones.

In interface, parameter size_t offset is not useful for transient_instance. Ideally need to know symbol and index, to implement remapping.
Also transient_instance have to bypass binding checks (and maybe array size checks): binding won't be valid, as it's a generic proxy-class.

I've considered to make solution based on virtual [const] void* data() - works for normal/opaque; but impossible to implement for transient.

@Try Try requested a review from lmichaelis August 15, 2023 21:38
@lmichaelis
Copy link
Member

I've considered to make solution based on virtual [const] void* data() - works for normal/opaque; but impossible to implement for transient.

Okay, I'm happy with that 👍. We're good to merge after making get_{float, int} and set{float, int, string} private or protected.

@lmichaelis lmichaelis added enhancement New feature or request modded Issues or features related to modifications of Gothic labels Aug 17, 2023
put get_{float, int} and set{float, int, string} to protected section;
reinvent interface for transient_instance
@Try
Copy link
Contributor Author

Try commented Aug 17, 2023

Okay, I'm happy with that 👍. We're good to merge after making get_{float, int} and set{float, int, string} private or protected.

This is done; also decide to change transient_instance a bit and make it more consistent with everything else.

@lmichaelis lmichaelis merged commit a348a38 into GothicKit:main Aug 19, 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 modded Issues or features related to modifications of Gothic
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants