Optimize the write barriers for GC stores
Categories
(Core :: JavaScript: WebAssembly, enhancement, P3)
Tracking
()
People
(Reporter: rhunt, Unassigned)
References
Details
We perform 'barrier' operations on writes to allow the GC intercept how we are mutating the heap. We let the GC barrier before a write (pre-barrier) so that it can mark an object that's last reference is about to be overwritten when it is doing an incremental GC. We let the GC barrier after a write (post-barrier) so that it can see if a new pointer to a nursery object was added, which will need to become a root for a future minor GC collection of the nursery.
Right now our post-write barriers are sub-optimal in baseline and Ion. In general, there are several conditions we can check that will determine if the post-write barrier is not needed [1]. If those conditions are not met, we need to make a call into the GC for it to update the store buffer.
Baseline currently performs this filtering to avoid the call in the common case [2], but due to how it makes the call, it must spill all registers before performing any filtering [3] which lowers the gain we get in the common case.
Ion currently does no filtering at all and does an unconditional call [4].
We should try to have the following on baseline and Ion:
- Perform filtering to skip the barrier if possible
- If that fails, jump to an out of line code block to perform the barrier
- Spill all volatile registers so that the common case doesn't need to spill assuming a call will happen
- Make the call to the GC to perform the barrier
- Restore the volatile registers and rejoin from the out of line code block
The MPostWriteBarrier instruction used by JS actually does all of this. It'd be nice to re-use this, but there are enough JS specific things in there that I don't think it's possible. We could create our own MWasmPostWriteBarrier which does exactly what we need, modeled after MPostWriteBarrier.
As for pre-write barriers, we currently do some filtering followed by a possible call. The call performs spilling/restoring of volatile registers, which is nice. The one improvement we could make here would be to move the call out-of-line. The pre-write barrier implementation is essentially the same on Ion/baseline. In Ion, it's done as part of MWasmStoreRef [5].
It's also possible that we should focus just on optimizing Ion and not baseline, assuming that execution time of baseline isn't as important.
[1] https://searchfox.org/mozilla-central/rev/99f83e78415867726a5076a525e4a60840f3be33/js/src/wasm/WasmGC.cpp#241
[2] https://searchfox.org/mozilla-central/rev/99f83e78415867726a5076a525e4a60840f3be33/js/src/wasm/WasmBaselineCompile.cpp#6208
[3] https://searchfox.org/mozilla-central/rev/99f83e78415867726a5076a525e4a60840f3be33/js/src/wasm/WasmBaselineCompile.cpp#6203
[4] https://searchfox.org/mozilla-central/rev/99f83e78415867726a5076a525e4a60840f3be33/js/src/wasm/WasmIonCompile.cpp#1798
[5] https://searchfox.org/mozilla-central/rev/99f83e78415867726a5076a525e4a60840f3be33/js/src/jit/CodeGenerator.cpp#8303
| Reporter | ||
Comment 1•3 years ago
|
||
Another piece is our 'precise' vs 'imprecise' barriers. The generational GC has store buffer entries pointing at references in the heap that may contain pointers into the nursery. When we do a post-write barrier, the precise variants will attempt to remove a store buffer entry if the newly written pointer is not into the nursery, but the old one was.
This is important for wasm tables, where their memory can be re-allocated and moved by C++ code which is not aware of barriers. For all our other objects, the imprecise version works just fine. Ion currently only uses the precise version because it mainly writes to tables. With GC objects, we should add the imprecise version back.
| Reporter | ||
Comment 2•3 years ago
|
||
This was completed in bug 1814519.
Description
•