WebAssembly / gc

Branch of the spec repo scoped to discussion of GC integration in WebAssembly
https://webassembly.github.io/gc/
Other
1k stars 72 forks source link

Spec module global instantiation is incorrect #571

Open CharlieTap opened 2 weeks ago

CharlieTap commented 2 weeks ago

As globals can now have initial expressions that point to other globals, it's not possible to compute all the initialiser expressions into a vector ahead of global allocation.

For example

(module (global $g i32 (i32.const 0)) (global i32 (global.get $g)))

The second initialiser here would call global.get on a yet to be allocated global

Instead we should probably do

for each global in module

  1. compute initial value
  2. allocate global
tlively commented 2 weeks ago

If you allocate the vector to hold the globals and then evaluate the initializer expressions, there's no problem, right? Is there a specific part of the spec that says something incorrect?

rossberg commented 2 weeks ago

The Instantiation section actually has a note addressing this:

[...] Similarly, module allocation and the evaluation of global and table initializers as well as element segments are mutually recursive because the global initialization values valg, ref t, and element segment contents (ref )* are passed to the module allocator while depending on the module instance moduleinst and store 𝑆′ returned by allo- cation. Again, this recursion is just a specification device. In practice, the initialization values can be determined beforehand by staging module allocation such that first, the module’s own function instances are pre-allocated in the store, then the initializer expressions are evaluated in order, allocating globals on the way, then the rest of the module instance is allocated, and finally the new function instances’ module fields are set to that module instance. This is possible because validation ensures that initialization expressions cannot actually call a function, only take their reference.

That said, I have plans to refactor the module rules to be more sequential, but that's currently low priority.

CharlieTap commented 2 weeks ago

Hey @tlively

Sure so here on allocation it mentions passing in the global initializer values as a prior, it's impossible to compute these before allocation because their expressions can call global get and globals are not yet allocated.

The allocation function for modules requires a suitable list of external values that are assumed to match the import vector of the module, a list of initialization values for the module’s globals, and list of reference vectors for the module’s element segments.

Let be the module to allocate and the vector of external values providing the module’s imports, the initialization values of the module’s globals, the initializer reference of the module’s tables, and the reference vectors of the module’s element segments.

... For each global in , do:

Let be the global type obtained by instantiating in defined below.

Let be the global address resulting from allocating with initializer value .

CharlieTap commented 2 weeks ago

This also applies to element segment initial expressions also in the spec, data segments are already after module allocation so they are fine as they are.

@rossberg It's actually a slightly different problem to what you linked, the allocation depending on a partial instance problem has been the case prior to the gc spec. This issue specifically relates global initializer values and element segment values now including global.get as a const express and thus having to come after global allocation