Visitar URL original
[JSC] Load each private name once in straight-line bytecode by sosukesuzuki · Pull Request #76308 · WebKit/WebKit · GitHub
Skip to content

[JSC] Load each private name once in straight-line bytecode - #76308

Open
sosukesuzuki wants to merge 1 commit into
WebKit:mainfrom
sosukesuzuki:eng/load-each-private-name-once-in-straight-line-bytecode
Open

sosukesuzuki wants to merge 1 commit into
WebKit:mainfrom
sosukesuzuki:eng/load-each-private-name-once-in-straight-line-bytecode

Conversation

@sosukesuzuki

@sosukesuzuki sosukesuzuki commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

302131c

[JSC] Load each private name once in straight-line bytecode
https://bugs.webkit.org/show_bug.cgi?id=326801

Reviewed by NOBODY (OOPS!).

Every private field access loads the private name symbol from the class
scope with resolve_scope and get_from_scope, so `this.#x * other.#x`
loads the same symbol twice.

This patch keeps a loaded private name in a register and reuses it until
a label is emitted, since code after a label can be reached without
passing the load. The registers are reserved when BytecodeGenerator is
constructed, so they sit below the temporaries used by expressions and
are neither interleaved with the operands of new_array or strcat nor
overwritten by a callee frame. To know how many to reserve, the parser
counts the private names used by the function. At most 16 registers are
reserved, and further names are loaded into a temporary as before.

The loaded names are also dropped when a class with private names is
entered or left, since it may declare the same name.
m_seenPrivateNameUseInNonReparsingFunctionMode is renamed to
m_seenPrivateNameUse because it is now also read while reparsing a
function.

Test: JSTests/stress/private-name-register-reuse.js

* JSTests/stress/private-name-register-reuse.js: Added.
(shouldBe):
(join):
(Point):
(Point.count):
(Point.sub):
(Point.prototype.method):
(Point.prototype.get accessor):
(Point.prototype.dot):
(Point.prototype.array):
(Point.prototype.template):
(Point.prototype.concat):
(Point.prototype.call):
(Point.prototype.nestedCall):
(Point.prototype.acrossCall):
(Point.prototype.spread):
(Point.prototype.construct):
(Point.prototype.conditional):
(Point.prototype.branch):
(Point.prototype.logical):
(Point.prototype.optional):
(Point.prototype.loop):
(Point.prototype.labeled):
(Point.prototype.select):
(Point.prototype.tryCatch):
(Point.prototype.tryFinally):
(Point.prototype.has):
(Point.prototype.write):
(Point.prototype.swap):
(Point.prototype.mixed):
(Point.prototype.generator):
(Point.prototype.async asynchronous):
(Point.prototype.arrow):
(Point.prototype.defaultParameter):
(Point.prototype.evaluate):
(Point.prototype.shadow.try.Inner):
(Point.prototype.shadow):
(Many.prototype.sum):
(Many.prototype.array):
(Many):
(testStraightLine):
(testCalls):
(testControlFlow):
(i.p.asynchronous.then):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::GenericLabel<JSGeneratorTraits>::setLocation):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitLoadPrivateName):
(JSC::BytecodeGenerator::pushPrivateAccessNames):
(JSC::BytecodeGenerator::popPrivateAccessNames):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::invalidateLoadedPrivateNames):
* Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:
(JSC::BaseDotNode::emitGetPropertyValue):
(JSC::BaseDotNode::emitPutProperty):
(JSC::PostfixNode::emitDot):
(JSC::PrefixNode::emitDot):
(JSC::InNode::emitBytecode):
(JSC::DefineFieldNode::emitBytecode):
* Source/JavaScriptCore/parser/Nodes.h:
(JSC::ScopeNode::usedPrivateNameCount const):
(JSC::ScopeNode::setUsedPrivateNameCount):
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseBinaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):
* Source/JavaScriptCore/parser/Parser.h:
(JSC::Parser<LexerType>::parse):

302131c

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win
✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ✅ 🧪 win-tests
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🧪 jsc-wpe
✅ 🧪 jsc-x86-64 ✅ 🧪 api-ios ✅ 🧪 mac-wk2 ✅ 🛠 gtk3-gcc
✅ 🛠 🧪 jsc-debug-arm64 ✅ 🛠 ios-safer-cpp ✅ 🧪 mac-AS-debug-wk2 ✅ 🛠 gtk
✅ 🛠 vision ✅ 🧪 gtk-wk2
✅ 🛠 vision-sim ✅ 🧪 mac-intel-wk2 ✅ 🧪 api-gtk
✅ 🧪 vision-wk2 ✅ 🛠 mac-safer-cpp ✅ 🛠 playstation
✅ 🛠 tv ✅ 🧪 mac-site-isolation
✅ 🛠 tv-sim
✅ 🛠 watch
✅ 🛠 watch-sim

@sosukesuzuki
sosukesuzuki requested a review from a team as a code owner October 8, 2026 12:36
@sosukesuzuki sosukesuzuki self-assigned this Oct 8, 2026
@sosukesuzuki
sosukesuzuki marked this pull request as draft October 8, 2026 12:37
https://bugs.webkit.org/show_bug.cgi?id=326801

Reviewed by NOBODY (OOPS!).

Every private field access loads the private name symbol from the class
scope with resolve_scope and get_from_scope, so `this.#x * other.#x`
loads the same symbol twice.

This patch keeps a loaded private name in a register and reuses it until
a label is emitted, since code after a label can be reached without
passing the load. The registers are reserved when BytecodeGenerator is
constructed, so they sit below the temporaries used by expressions and
are neither interleaved with the operands of new_array or strcat nor
overwritten by a callee frame. To know how many to reserve, the parser
counts the private names used by the function. At most 16 registers are
reserved, and further names are loaded into a temporary as before.

The loaded names are also dropped when a class with private names is
entered or left, since it may declare the same name.
m_seenPrivateNameUseInNonReparsingFunctionMode is renamed to
m_seenPrivateNameUse because it is now also read while reparsing a
function.

Test: JSTests/stress/private-name-register-reuse.js

* JSTests/stress/private-name-register-reuse.js: Added.
(shouldBe):
(join):
(Point):
(Point.count):
(Point.sub):
(Point.prototype.method):
(Point.prototype.get accessor):
(Point.prototype.dot):
(Point.prototype.array):
(Point.prototype.template):
(Point.prototype.concat):
(Point.prototype.call):
(Point.prototype.nestedCall):
(Point.prototype.acrossCall):
(Point.prototype.spread):
(Point.prototype.construct):
(Point.prototype.conditional):
(Point.prototype.branch):
(Point.prototype.logical):
(Point.prototype.optional):
(Point.prototype.loop):
(Point.prototype.labeled):
(Point.prototype.select):
(Point.prototype.tryCatch):
(Point.prototype.tryFinally):
(Point.prototype.has):
(Point.prototype.write):
(Point.prototype.swap):
(Point.prototype.mixed):
(Point.prototype.generator):
(Point.prototype.async asynchronous):
(Point.prototype.arrow):
(Point.prototype.defaultParameter):
(Point.prototype.evaluate):
(Point.prototype.shadow.try.Inner):
(Point.prototype.shadow):
(Many.prototype.sum):
(Many.prototype.array):
(Many):
(testStraightLine):
(testCalls):
(testControlFlow):
(i.p.asynchronous.then):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::GenericLabel<JSGeneratorTraits>::setLocation):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitLoadPrivateName):
(JSC::BytecodeGenerator::pushPrivateAccessNames):
(JSC::BytecodeGenerator::popPrivateAccessNames):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::invalidateLoadedPrivateNames):
* Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:
(JSC::BaseDotNode::emitGetPropertyValue):
(JSC::BaseDotNode::emitPutProperty):
(JSC::PostfixNode::emitDot):
(JSC::PrefixNode::emitDot):
(JSC::InNode::emitBytecode):
(JSC::DefineFieldNode::emitBytecode):
* Source/JavaScriptCore/parser/Nodes.h:
(JSC::ScopeNode::usedPrivateNameCount const):
(JSC::ScopeNode::setUsedPrivateNameCount):
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseBinaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):
* Source/JavaScriptCore/parser/Parser.h:
(JSC::Parser<LexerType>::parse):
@sosukesuzuki
sosukesuzuki force-pushed the eng/load-each-private-name-once-in-straight-line-bytecode branch from 35b4a4f to 302131c Compare October 9, 2026 05:20
@sosukesuzuki
sosukesuzuki marked this pull request as ready for review October 9, 2026 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants