Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 5330149c555921d5eefddaa34d3c8a6a1fbd55ce
https://github.com/WebKit/WebKit/commit/5330149c555921d5eefddaa34d3c8a6a1fbd55ce
Author: Sosuke Suzuki <[email protected]>
Date: 2026-09-29 (Tue, 29 Sep 2026)
Changed paths:
A JSTests/microbenchmarks/destructuring-string.js
A JSTests/microbenchmarks/for-of-string-break.js
A JSTests/microbenchmarks/for-of-string-small.js
A JSTests/stress/for-of-fast-string-and-iterator-with-known-structure.js
A JSTests/stress/for-of-fast-string-and-other-iterables.js
A JSTests/stress/for-of-fast-string-close-fresh-realm-assignment.js
A JSTests/stress/for-of-fast-string-close-fresh-realm-destructuring.js
A JSTests/stress/for-of-fast-string-close-fresh-realm-loops.js
A JSTests/stress/for-of-fast-string-close-fresh-realm-replaced-methods.js
A JSTests/stress/for-of-fast-string-code-points.js
A JSTests/stress/for-of-fast-string-destructuring.js
A JSTests/stress/for-of-fast-string-generator.js
A JSTests/stress/for-of-fast-string-osr-late-close.js
A JSTests/stress/for-of-fast-string-osr-long-loop.js
A JSTests/stress/for-of-fast-string-wide-operands.js
M Source/JavaScriptCore/bytecode/BytecodeList.rb
M Source/JavaScriptCore/bytecode/IterationModeMetadata.h
M Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
M Source/JavaScriptCore/jit/BaselineJITRegisters.h
M Source/JavaScriptCore/jit/JITCall.cpp
M Source/JavaScriptCore/jit/JITOperations.cpp
M Source/JavaScriptCore/jit/JITOperations.h
M Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
M Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
M Source/JavaScriptCore/runtime/CommonSlowPaths.h
M Source/JavaScriptCore/runtime/JSGlobalObject.h
M Source/JavaScriptCore/runtime/JSStringIterator.h
M Source/JavaScriptCore/runtime/VM.cpp
M Source/JavaScriptCore/runtime/VM.h
Log Message:
-----------
[JSC] Iterate Strings without allocating a `JSStringIterator`
https://bugs.webkit.org/show_bug.cgi?id=325452
Reviewed by Yusuke Suzuki.
321765@main stopped allocating a JSArrayIterator in FastArray mode by keeping
the state of the iteration in registers. FastString mode (for-of and
destructuring over a String) still allocates a JSStringIterator every time in
LLInt, Baseline and DFG, and it is unobservable for the same reason: the next
method was already looked up when the iterator was opened, and while
stringIteratorProtocolWatchpointSet is valid, "return" is guaranteed to be
absent from the prototype chain.
This patch does the same for FastString mode. iterator holds a sentinel of its
own, next holds the index as an Int32, and iterable holds the string as before.
op_iterator_next and op_iterator_close_check compare iterator with the sentinel
for Arrays first and then with the one for Strings. When it is the latter,
op_iterator_close_check looks at stringIteratorProtocolWatchpointSet, and
creates the JSStringIterator if it is no longer valid.
With JSC_useDFGJIT=false:
Baseline
Patched
for-of-string-small 7.5573+-0.1228 ^
6.6576+-0.1222 ^ definitely 1.1351x faster
for-of-string-break 4.6593+-0.0620 ^
4.2070+-0.1624 ^ definitely 1.1075x faster
destructuring-string 25.8768+-0.2164 ^
21.5336+-0.2443 ^ definitely 1.2017x faster
string-iterator-for-of-ascii 149.8134+-1.0006 ^
140.7087+-1.9843 ^ definitely 1.0647x faster
With JSC_useFTLJIT=false:
Baseline
Patched
for-of-string-small 2.2048+-0.0344 ^
1.7072+-0.0611 ^ definitely 1.2914x faster
for-of-string-break 1.7627+-0.0699 ^
1.3706+-0.0314 ^ definitely 1.2860x faster
destructuring-string 7.4364+-0.1370 ^
5.9434+-0.1328 ^ definitely 1.2512x faster
string-iterator-for-of-ascii 44.7161+-0.6120 ^
37.0484+-0.3477 ^ definitely 1.2070x faster
Tests: JSTests/microbenchmarks/destructuring-string.js
JSTests/microbenchmarks/for-of-string-break.js
JSTests/microbenchmarks/for-of-string-small.js
JSTests/stress/for-of-fast-string-and-iterator-with-known-structure.js
JSTests/stress/for-of-fast-string-and-other-iterables.js
JSTests/stress/for-of-fast-string-close-fresh-realm-assignment.js
JSTests/stress/for-of-fast-string-close-fresh-realm-destructuring.js
JSTests/stress/for-of-fast-string-close-fresh-realm-loops.js
JSTests/stress/for-of-fast-string-close-fresh-realm-replaced-methods.js
JSTests/stress/for-of-fast-string-code-points.js
JSTests/stress/for-of-fast-string-destructuring.js
JSTests/stress/for-of-fast-string-generator.js
JSTests/stress/for-of-fast-string-osr-late-close.js
JSTests/stress/for-of-fast-string-osr-long-loop.js
JSTests/stress/for-of-fast-string-wide-operands.js
* JSTests/microbenchmarks/destructuring-string.js: Added.
(test):
* JSTests/microbenchmarks/for-of-string-break.js: Added.
(test):
* JSTests/microbenchmarks/for-of-string-small.js: Added.
(test):
* JSTests/stress/for-of-fast-string-and-iterator-with-known-structure.js: Added.
(shouldBe):
(second):
(collect):
(breakAtTwo):
(firstTwo):
* JSTests/stress/for-of-fast-string-and-other-iterables.js: Added.
(shouldBe):
(breakAt):
(firstTwo):
(Symbol.iterator):
(breakAtTwo):
(breakAtTwoAgain):
(shouldThrowTypeError):
* JSTests/stress/for-of-fast-string-close-fresh-realm-assignment.js: Added.
(shouldBe):
(installReturn.where.return):
(installReturn):
(destructureAssign):
(let.o.set a):
(destructureSetter):
(destructureThrow):
(destructureNested):
(const.noHook):
(run):
* JSTests/stress/for-of-fast-string-close-fresh-realm-destructuring.js: Added.
(shouldBe):
(installReturn.StringIteratorPrototype.return):
(installReturn):
(firstTwo):
(firstOnly):
(none):
(const.noHook):
(run):
* JSTests/stress/for-of-fast-string-close-fresh-realm-loops.js: Added.
(shouldBe):
(installReturn.where.return):
(installReturn):
(breakOut):
(returnOut):
(throwOut):
(continueOuter):
(finallyOut):
(runToEnd):
(nested):
(each):
(generatorReturn):
(const.noHook):
(run):
* JSTests/stress/for-of-fast-string-close-fresh-realm-replaced-methods.js:
Added.
(shouldBe):
(installReturn.StringIteratorPrototype.return):
(installReturn):
(StringIteratorPrototype.next):
(nextReplacedMidLoop):
(String.prototype.Symbol.iterator):
(symbolIteratorReplacedMidLoop):
(breakAtTwo):
(const.noHook):
* JSTests/stress/for-of-fast-string-code-points.js: Added.
(shouldBe):
(breakAt):
(nested):
(reassigned):
(rope):
* JSTests/stress/for-of-fast-string-destructuring.js: Added.
(shouldBe):
(firstAndRest):
(third):
(none):
(swapped):
(parameter):
* JSTests/stress/for-of-fast-string-generator.js: Added.
(shouldBe):
(eachTwice):
(firstTwo):
(async eachAwaited):
* JSTests/stress/for-of-fast-string-osr-late-close.js: Added.
(shouldBe):
(find):
(swap):
(StringIteratorPrototype.return):
* JSTests/stress/for-of-fast-string-osr-long-loop.js: Added.
(shouldBe):
(makeLong):
(count):
(find):
(swap):
(noInline):
* JSTests/stress/for-of-fast-string-wide-operands.js: Added.
(shouldBe):
(installReturn.StringIteratorPrototype.return):
(const.noHook):
* Source/JavaScriptCore/bytecode/BytecodeList.rb:
* Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::handleIteratorOpen):
(JSC::DFG::ByteCodeParser::handleIteratorNext):
* Source/JavaScriptCore/jit/JITCall.cpp:
(JSC::JIT::emit_op_iterator_next):
(JSC::JIT::emit_op_iterator_close_check):
* Source/JavaScriptCore/jit/JITOperations.cpp:
(JSC::JSC_DEFINE_JIT_OPERATION):
* Source/JavaScriptCore/jit/JITOperations.h:
* Source/JavaScriptCore/llint/LowLevelInterpreter64.asm:
* Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:
(JSC::iteratorOpenTryFastImpl):
(JSC::iteratorNextTryFastImpl):
(JSC::iteratorNextFastStringImpl):
(JSC::JSC_DEFINE_COMMON_SLOW_PATH):
* Source/JavaScriptCore/runtime/CommonSlowPaths.h:
* Source/JavaScriptCore/runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::offsetOfStringIteratorProtocolWatchpointSet):
* Source/JavaScriptCore/runtime/JSStringIterator.h:
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::VM::VM):
(JSC::VM::visitAggregateImpl):
* Source/JavaScriptCore/runtime/VM.h:
(JSC::VM::fastStringSentinel):
(JSC::VM::fastStringValuesSentinel): Deleted.
Canonical link: https://commits.webkit.org/322172@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications