SpiderMonkey is the JavaScript engine used in Mozilla Firefox. This newsletter gives an overview of the JavaScript and WebAssembly work we’ve done as part of the Firefox 88 and 89 Nightly release cycles.
In this newsletter we bid a fond farewell to module owner emeritus Jason Orendorff, and say hello to Jan de Mooij as the new JavaScript Engine module owner.
If you like these newsletters, you may also enjoy Yulia’s Compiler Compiler live stream.
🏆 New contributors
We’d like to thank our new contributors. We are working with Outreachy for the May 2021 cohort, and so have been fortunate enough to have more than the usual number of new contributors.
- Sneha K fixed three bugs (🎉) by doing some refactoring around parser scopes, and removing an old duplicate API.
- Yohei improved several SIMD instructions by implementing constant inlining.
- Shariff removed use of an old macro
- Ashita fixed three bugs (🎉) cleaned up some unused enum values, removed an old macro from a few files, and fixed the naming schema for Warp’s tooling.
- Ashwini also cleaned up some uses of the JSID macros.
- Pavel also removed uses of the JSID_IS_ATOM macro.
- Bukola Akinnadeju cleaned up some uses of the JSID macros.
- Jane Kotovich also worked on the JSID macro removal project.
👷🏽♀️ JS features
- We enabled Top-Level Await for Firefox 89.
- We implemented the RegExp Match Indices proposal for Firefox 88.
- Intl
DisplayNames
andListFormat
were changed to use the newGetOption
behavior. - Contributor Jonatan is working on the Import Assertions proposal, and is landing preliminary code for that.
- We landed a number of
ReadableStream
changes to match the new WebIDL semantics.
⚡ WebAssembly
- We enabled support for large ArrayBuffers and 4 GB Wasm memories in Firefox 89.
- We enabled support for SIMD on x86 and x64 in Firefox 89.
- Igalia finished the implementation of the Exception Handling proposal in the Baseline Compiler.
- We implemented support for arrays and rtt-based downcasting in our Wasm GC prototype.
- We’ve enabled the Ion backend for ARM64 in Nightly builds.
- We’ve landed many changes and optimizations for SIMD support.
- We removed various prefs for features we’ve been shipping for some time.
❇️ Stencil
Stencil is our project to create an explicit interface between the frontend (parser, bytecode emitter) and the rest of the VM, decoupling those components. This lets us improve web-browsing performance, simplify a lot of code and improve bytecode caching.
- We implemented a mechanism for function delazification information to be merged with the initial stencil before writing to caches.
- We added support for modules and off-thread compilation to the Stencil API.
- We optimized use of
CompilationState
in the parser for certain cases. - We added magic values to the Stencil bytecode serialization format to detect corrupt data and handle this more gracefully.
- We fixed the Stencil bytecode serialization format to deduplicate bytecode.
- We’re getting closer to sharing Stencil information for self-hosted code across content processes. We expect significant memory usage and performance improvements from this in the coming weeks.
🧹 Garbage Collection
- We simplified and optimized the
WeakMap
code a bit. - We disabled nursery poisoning for Nightly release builds. The poisoning was pretty expensive and often caused slowdowns compared to release builds that didn’t have the poisoning.
- We added support for decommitting free arenas on Apple’s M1 hardware. This required some changes due to the 16 KB page size.
- We changed the pre-write barrier to use a buffering mechanism instead of marking directly.
- GC markers now describe what they are, hopefully reducing confusion over whether the browser is paused throughout a major GC
🚀 JIT
- We changed how
arguments
objects are optimized. Instead of doing an (expensive) analysis for all functions that usearguments
, we now use Scalar Replacement in the Warp backend to optimize awayarguments
allocations. The new implementation is simpler, more self-contained, and lets us avoid doing the analysis for cold functions. - We fixed the Scalar Replacement code for arrays and objects to work with Warp.
- We also added back support for branch pruning with Warp.
- We added CacheIR support for optimizing
GetElem
,SetElem
andin
operations with null or undefined property keys. This turned out to be very common on certain websites. - We optimized DOM getters for
window.foo
(WindowProxy
objects). - We improved function inlining in Warp for certain self-hosted functions (for example
Array.prototype.map
) that benefit from inlining. - We added a browser pref to control the function inlining size threshold, to help us investigate performance issues.
📐 ReShape
Now that Warp is on by default and we’ve removed the old backend and Type Inference mechanism, we’re able to optimize our object representation more. Modern websites spend a significant amount of time doing property lookups, and property information takes up a lot of space, so we expect improvements in this area to pay off.
- We’ve merged
ObjectGroup
(used by the old Type Inference system) intoShape
andBaseShape
. This removed a word from every JS object and is also simpler. - We cleaned up and deduplicated our property lookup code.
- We’ve replaced the old
JSGetterOp
andJSSetterOp
getters/setters with a property attribute. - We changed our implementation of getter/setter properties: instead of storing the getter and setter objects in the shape tree, we now store them in object slots. This fixes some performance cliffs and unblocks future Shape changes.
- We’ve started adding better abstractions for property information stored in shapes. This will make it easier to experiment with different representations in the coming weeks.
🛠 Testing
- We made SpiderMonkey’s test suites on Android about four times faster by optimizing the test runner, copying fewer files to the device, and reducing the number of jit-test configurations.
- We removed the Rust API crates because upstream Servo uses its own version instead of the one we maintained in-tree.
- We landed support for the Fuzzilli JS engine fuzzer in the JS shell.
📚 Miscellaneous
- We cleaned up the lexical environment class hierarchy.
- We optimized
Object.assign
. Modern JS frameworks use this function a lot. - The bytecode emitter now emits optimized bytecode for name lookups in strict-mode
eval
. - We updated irregexp to the latest upstream version.
- We optimized checks for strings representing an index by adding a flag for this to atoms.
- Function delazification is now properly reported in the profiler.
- The profiler reports more useful stacks for JS code because it’s now able to retrieve registers from the JIT trampoline to resume stack walking.
- We added memory reporting for external
ArrayBuffer
memory and also reduced heap-unclassified memory by adding some new memory reporters. - We added documentation for the
LifoAlloc
allocator. - We fixed Clang static analysis and formatting issues in the Wasm code.
- We’ve started cleaning up
PropertyDescriptor
by usingMaybe<PropertyDescriptor>
.