Skip to content

Conversation

@abelstuker
Copy link
Contributor

Closes #299.

This extends the implementation of global variables in the VM with mutability and import / export semantics.
It provides encapsulation of globals, mutability checks and import resolution.
This change restructures the global variable to align with the accepted proposal https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md.

The host program can now define globals for import in the module as follows:

// Define some mutable global state (initialized to 0)
def_glob(some_mutable_state, I32, true, {.int32 = 0});
// Install the global
install_global(some_mutable_state);
;; Import the mutable global state
(global $some_mutable_state (import "env" "some_mutable_state") i32)

;; Modify the mutable global in a function
(global.set $some_mutable_state 2)

Conversely, the host can also access globals exported in the module:

;; Export some global state (initialized to 3)
(global $some_other_state (mut i32) (i32.const 3))
(export "some_other_state" (global $some_other_state))
// Retrieve the mutable global
uint32_t some_other_state_idx = this->get_export_global_idx(m, "some_other_state");
Global *some_other_state = m->globals[some_other_state_idx];
// Modify the mutable global
some_other_state->value->value.uint32 = 5;

@abelstuker abelstuker marked this pull request as ready for review November 14, 2025 20:50
Copy link
Member

@tolauwae tolauwae left a comment

Choose a reason for hiding this comment

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

Looks pretty good, I made a few comments.
However, I realised while reviewing that we can't properly test this without multiple module support (#27), right?

// The primitive table
PrimitiveEntry primitives[ALL_PRIMITIVES];
// The globals table
Global globals[ALL_GLOBALS];
Copy link
Member

Choose a reason for hiding this comment

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

Is this array not always length 0 now? I think we need a dynamic array here since we only know how many globals will be exported at runtime (when loading the wasm module). Maybe I am missing something here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes indeed, for now all these arrays of globals are length 0 (not only in arduino.cpp, but also in the emulated.cpp, idf.cpp and zephyr.cpp). These arrays are intended only for globals that are defined by the host, available for import within the Wasm module (those exported by the module are not handled here). Since the number of such host-defined globals is known at compile time this array size can be statically determined. Currently, there are no such globals, but it is possible to add such importable globals in the future (e.g. status flags provided by the host ...).

On the contrary, the other, not imported globals in the module are indeed only known at runtime. Therefore, these globals are dynamically allocated during instantiation of the Wasm module (see case 6 of the big switch within the WARDuino::instantiate_module function in WARDuino.cpp). These globals can either stay local to the module or be exported in the export section. When exported, the host can retrieve the exported global by first retrieving the index of the global using WARDuino::get_export_global_idx in WARDuino.cpp and then accessing that global at that obtained index from the globals in the Module struct.

To summarize, the host-defined (importable) globals are statically known (and therefore also their array size). The module-defined (exportable) globals are dynamically allocated during instantiation.


// Global index for installing primitives
#define NUM_GLOBALS 1
#define ALL_GLOBALS NUM_GLOBALS
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the globals are part of the primitives. Can we move this to the interpreter or the WARDuino class? Now it will only get loaded on Arduino.

Copy link
Contributor Author

@abelstuker abelstuker Nov 22, 2025

Choose a reason for hiding this comment

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

Globals can be defined by the host (e.g. status flags). These globals can then be imported in the Wasm module. As mentioned in the other comment, the number of host-defined globals is statically known. Similar to the host-defined function primitives, this host-defined globals functionality is added not only for arduino.cpp, but also for emulated.cpp, idf.cpp and zephyr.cpp. These platforms should not necessarily have the exact same host-defined globals.

Did I understand your comment correctly?

P.S. The NUM_GLOBALS 1 is incorrect and should be NUM_GLOBALS 0. It is a leftover from me testing the functionality of these host-defined globals. EDIT: corrected in 2dd47af.

@abelstuker
Copy link
Contributor Author

I realised while reviewing that we can't properly test this without multiple module support (#27)

It is possible to test this since globals cannot only be shared between modules but also between a module and the host.
So a global that is defined by the host (for example in arduino.cpp, using def_glob and install_global) can be imported by the module. Or the other way around, a global exported by the module can be used by the VM using WARDuino::get_export_global_idx in WARDuino.cpp.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📑 Import/Export of Mutable Globals

3 participants