JSC PowerSort Galloping Merge
cd9b986
Source/JavaScriptCore/runtime/StableSort.h
-template<typename T, typename Compare>
-static void extendRunRight(T* array, size_t& right, size_t last, const Compare& compare)
+template<typename T, typename Compare>
+static void extendAndNormalizeRun(T* array, size_t& right, size_t last, const Compare& compare)
{
- // Only extends ascending runs
+ // Detect descending run and reverse in place to normalize
+ bool descending = ...
+ if (descending)
+ std::reverse(array + start, array + right + 1);
}
+template<typename T, typename Compare>
+static size_t gallopLeft(T* array, size_t base, size_t len, const T& key, const Compare& compare)
+{
+ // Exponential probe forward, then binary search to find insertion point
+ size_t lastOfs = 0, ofs = 1;
+ while (ofs < len && compare(array[base + ofs], key))
+ lastOfs = ofs, ofs <<= 1;
+ ofs = std::min(ofs, len);
+ // binary search in [lastOfs, ofs]
+ ...
+}
+template<typename T, typename Compare>
+static size_t gallopRight(T* array, size_t base, size_t len, const T& key, const Compare& compare)
+{
+ // Mirror of gallopLeft — probe for elements that should follow key
+ ...
+}
PowerSort is JSC's implementation of Array.prototype.sort (and TypedArray sort) — a natural merge sort variant that identifies pre-sorted "runs" in the input and merges them in an order determined by a merge tree. This commit introduces three optimizations: it eliminates the initial unnecessary buffer copy (src is now always the output, dst is only a working scratch), extends run detection to recognize and normalize descending runs via in-place reversal, and introduces galloping merge borrowed from Python's TimSort.
Galloping changes the inner merge loop: when one run dominates repeatedly (many consecutive elements all less-than the other run's current element), instead of comparing one-by-one, it does an exponential probe (1, 2, 4, 8… positions forward) to find a boundary, then bulk-copies the entire dominated region. Both the regular Array path (ArrayPrototype.cpp) and the TypedArray path (JSGenericTypedArrayViewPrototypeFunctions.h) are updated.
Standard merge inner loop: Galloping merge inner loop:
compare A[i] vs B[j] gallopRight: probe B at 1,2,4,8… positions
copy winner, advance one find k where B[k] >= A[i]
compare A[i] vs B[j+1] bulk-copy B[j..j+k] to output
...O(n) comparisons resume element-by-element
Significance
Yields a ~1.34x speedup on the magic-forest benchmark, and galloping introduces new bulk-copy code paths that interact with user-controlled comparator functions — a historically fertile area for type confusion and out-of-bounds bugs in JS engines.
Audit directions
a Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaaaa Aaaaa Aaaaaaa Aa Aaaaaaaaa Aaaaa a Aa Aaaa a Aa Aaaa a Aa Aaaa a Aaaa a Aaaaaaaaaa Aaaa Aaaaaaa Aaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaa Aa Aaaaaaaa a Aaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaaaa Aa Aaaa Aa Aaaaa Aaaa a Aaaaaaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaa Aaaaa Aaaaa a Aaa Aaaaa Aa Aaaaaaaa Aa Aaaa Aaaaaa Aaa Aaaaaaa Aaaaa Aaaa Aaaaaaaaaa Aaaaaaaaa
a Aaaaaaaaaaaaaaa Aaaaaa Aaaaa Aaaaaaaa Aaaaa Aaa Aaaaaaaaaaa Aaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaa Aa Aaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaa Aa Aaaaaaaaa Aaaaaaaa Aaaaaaaaaaa Aa Aaa Aaaaa Aaaaaaaaa Aaaaa Aaaaaaaa a Aa Aaa Aaaa a Aaa Aaaaa Aaaaa Aaaaa Aaaa Aaa Aaa Aa Aaa Aaaa
a Aaaaaaaaaaaa Aaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaa Aaaa Aaa Aaaa Aaa Aaaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaa Aaaaaaaaa Aaaa Aaaa Aaa Aaaaaaaa Aaa Aaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaa Aaa Aaa Aaaaa a Aaaaaaaaaa Aaaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaa Aaaaa Aaa Aaaaa Aaaaaaaa Aaa Aaaaaa Aaa Aaaa Aaaa Aaaaaaaa Aa a Aaaaaaaaa Aaaaaaaaaaaaaaaaa
a Aaaaaaaa Aaaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaaaaaaa Aaaaa Aaaaaa Aaaaa Aaa Aa Aaa Aaaaaa Aaa Aaaaaa Aaa Aaa Aa Aaa Aaaaaaaa Aaa Aaaa Aaaa Aaaa Aaaaaaaaaa Aaaaa Aa Aaa Aaaaaaaaa Aa Aa Aa Aaaaaaaaa Aaa Aaaaaa Aaaaaaaa Aa Aaa Aaaa Aaaaa a Aaaaaaaaaaaaaa Aaaaa Aaaaaa Aa Aaaaaa Aaaaa Aa Aaaaaa Aaaaaaaaaaa
🔒New bulk-copy paths with user-controlled callbacks and changed buffer ownership — several edge cases in this fast path are worth security investigation.
Subscribe to read more