commit 10bb1622d02686b10596948eb8ab258bca45eff9
parent 4d851b34a0aa8b743a08cb64cc00efcfbf2cb9e5
Author: Jan de Mooij <jdemooij@mozilla.com>
Date: Fri, 28 Nov 2025 11:46:07 +0000
Bug 2001986 - Optimize LiveRange::tryToMoveDefAndUsesInto a bit. r=jseward
In some JS profiles of popular websites (bug 2001273, bug 1936209) we were spending
quite a bit of time under `LiveRange::tryToMoveDefAndUsesInto` called from
the non-Wasm code path in `trySplitAcrossHotcode`.
This patch adds a fast path based on `moveAllUsesToTheEndOf`. Running some JS benchmarks
locally, this fast path hits in the majority of cases.
Differential Revision: https://phabricator.services.mozilla.com/D273829
Diffstat:
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp
@@ -835,6 +835,28 @@ void LiveRange::tryToMoveDefAndUsesInto(LiveRange* other) {
CodePosition otherFrom = other->from();
CodePosition otherTo = other->to();
+ // Copy the has-definition flag to |other|, if possible.
+ if (hasDefinition() && from() == otherFrom) {
+ other->setHasDefinition();
+ }
+
+ // If we have no uses, we're done.
+ if (!hasUses()) {
+ return;
+ }
+
+ // Fast path for when we can use |moveAllUsesToTheEndOf|. This is very common
+ // for the non-Wasm code path in |trySplitAcrossHotcode|. This fast path had a
+ // hit rate of 75% when running Octane in the JS shell.
+ //
+ // Note: the |!other->hasUses()| check could be more precise, but this didn't
+ // improve the hit rate at all.
+ if (!other->hasUses() && usesBegin()->pos >= otherFrom &&
+ lastUse()->pos < otherTo) {
+ moveAllUsesToTheEndOf(other);
+ return;
+ }
+
// The uses are sorted by position, so first skip all uses before |other|
// starts.
UsePositionIterator iter = usesBegin();
@@ -852,22 +874,21 @@ void LiveRange::tryToMoveDefAndUsesInto(LiveRange* other) {
}
MOZ_ASSERT_IF(iter, !other->covers(iter->pos));
-
- // Distribute the definition to |other| as well, if possible.
- if (hasDefinition() && from() == other->from()) {
- other->setHasDefinition();
- }
}
void LiveRange::moveAllUsesToTheEndOf(LiveRange* other) {
MOZ_ASSERT(&other->vreg() == &vreg());
MOZ_ASSERT(this != other);
- MOZ_ASSERT(other->contains(this));
+ MOZ_ASSERT(intersects(other));
if (uses_.empty()) {
return;
}
+ // Assert |other| covers all of our uses.
+ MOZ_ASSERT(other->covers(uses_.begin()->pos));
+ MOZ_ASSERT(other->covers(uses_.back()->pos));
+
// Assert |other->uses_| remains sorted after adding our uses at the end.
MOZ_ASSERT_IF(!other->uses_.empty(),
SortBefore(other->uses_.back(), *uses_.begin()));