Repository navigation
Bump WebKit (oven-sh/WebKit#946 preview): a RegExp match that the DFG folds on a JIT thread stays within the committed stack on Windows - #47416
Draft
robobun wants to merge 9 commits into
Conversation
…the committed stack (Windows) The test runs a copy of bun.exe whose PE header commits one page of stack for each thread. The DFG folds a match of a constant pattern against a constant string on a JIT thread, and the frame of the match is 128 KB. With the JavaScriptCore that main pins, the frame is written below the guard page of that thread and the process ends with exit code 0xC0000005. The WebKit change that this branch will pin gives the fold up instead.
…ows test machines A test file that fails on purpose on Windows and prints measurements: for a match that the DFG folds on a JIT thread, whether it is folded, kept or ends the process, by the size of its frame, by the stack commit of the executable and by the order of the compilations. To be removed before this pull request is ready.
Collaborator
Author
|
Updated 1:03 PM PT - Oct 30th, 2026
❌ @robobun, your commit 65c5d40 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 47416That installs a local version of the PR into your bun-47416 --bun |
Collaborator
Author
|
Status: draft. The diff is the pin and one test file. It waits for oven-sh/WebKit#946 to merge, and for
|
The build is the JavaScriptCore that main pins (8c6c963e3c49) with two changes. On Windows the stack limit of a JIT thread that runs RegExp JIT code is no lower than the stack that the thread has committed. And a JIT thread whose RegExp JIT code fails gives the fold of the match up. It does not run the interpreter. WEBKIT_VERSION must move to the merge commit of that pull request before this merges: a preview release goes away when its pull request closes.
…sh/WebKit#946 at 2f9450ab The measurements ran on the Windows test machines in builds 135536 (the JavaScriptCore that main pins) and 135602 (the preview build b4e7b533). Their numbers are in the pull request. The preview build 2f9450ab has the same engine as b4e7b533. The commit between them changes one test of JavaScriptCore.
…gine that main pins
… a group, and a RegExp that has bytecode
…e measurements are removed
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related to #45094
Problem
0xC0000005. bun.exe does that for a frame above its 2 MB of stack commit.yarr/YarrMatchingContextHolder.h:68).scripts/build/flags.ts:1137) to one page. Then a frame of 20 KB is enough.Fix
WEBKIT_VERSIONis a preview build of [JSC] Keep a compiler thread's RegExp fold within the committed stack on Windows, and fold only a match that was answered WebKit#946. Before this merges, move it to a commit of WebKitmainthat has it.test/js/bun/jsc/regexp-fold-jit-thread-stack.test.tson Windows x64 and aarch64. Exit code 5 on the pin ofmain(build 135863), passes here (build 135866).Background
Downsides
Notes
The pin, and what must happen before this merges
The preview build is the engine that
mainpins (8c6c963e3c49) plus the commits of oven-sh/WebKit#946 up to 6672ab7320. That is the last commit before that pull request merged WebKitmaininto its branch.mainhere does not pin WebKitmainyet: #47112 does that, and it updates the 383 test files that depend on the engine. A preview release goes away when its pull request merges or closes, so this pull request is a draft.mainhere pins WebKitmain. WebKitmainis ahead of the pin: it has the upstream upgrade of Upgrade to upstream WebKit e8f704815e94 WebKit#923, which Upgrade WebKit to e8f704815e94 #47112 brings here, and [JSC] AOT: handlers at the end of the stack, text constants in flow, samples in native code WebKit#934.WEBKIT_VERSIONto a commit of WebKitmainthat has [JSC] Keep a compiler thread's RegExp fold within the committed stack on Windows, and fold only a match that was answered WebKit#946. If another pull request already did that, only the test is left.#45094 names this limit as what removes its one new failure. The order of the two is for a maintainer to decide. The test here does not depend on #45094: it sets the stack commit of a copy of bun.exe itself.
The test
test/js/bun/jsc/regexp-fold-jit-thread-stack.test.tsis for Windows only. It has two tests. Each runs a script file in which the event loop calls a function untilnumberOfDFGCompilessays that the DFG has compiled it. So nothing inlines the function, and the JIT thread has finished with it before the script goes on. The match in each function has a constant pattern and a constant string. Each test expects one line for each function, an empty stderr and exit code 0. The loop has no limit of its own: a function that the DFG never compiles ends in the timeout of the test.BUN_JSC_*variables lower the JIT thresholds, because one call takes about a millisecond.0x1000inSizeOfStackCommitof its PE header. Four functions:testandexecwith a frame of 128 KB, a group that repeats 24 times (a frame of 14 KB, and as much again from the stack for each turn), and a 128 KB frame for a RegExp that has bytecode. For the last one the JIT thread runs the interpreter after its JIT code finds no room.mainExit code 5 is the low byte of
0xC0000005. The process ends on the JIT thread, below the guard pages, where no handler can run. The file takes about 1 second.The script is a file and not
-e:bun -ecompiles on the thread that runs JavaScript (JSC::Options::useConcurrentJIT() = falseinJSCInitialize).Earlier forms of the test gave the same results: build 135764 (the pin of
main, both tests fail), and builds 135536, 135602 and 135622 (only the copy with one page: fails on the pin ofmain, passes on the previews b4e7b533 and 2f9450ab).What folds, before and after
A temporary test file, now removed, printed these numbers on the Windows test machines. The program folds one match in a function that the event loop calls until the DFG has compiled it. One JIT thread. Whether the match is folded is read from the code of the function (
BUN_JSC_dumpDFGDisassembly=1). Build 135755 has the pin ofmain, build 135777 has the preview 7d7c45d0. The test file ran four times in each build, with five runs of each case, so each case has 20 runs on each machine.bun.exe as built. Its header has 2,097,152 bytes of stack commit.
mainA copy with one page of stack commit,
/^(a?){N}b$/.test("aaab"), 16 bytes of frame for each term. The sizes are 4, 4.8, 6, 7.2, 8.6, 10.2, 12.8, 16, 20, 24, 32, 48 and 128 KB.mainOther patterns, one page of stack commit:
main/^\p{RGI_Emoji}$/v, a frame of 21.7 KB, first functionWith this engine no run ended with an exit code other than 0.
The first measurements (builds 135536 and 135602, five runs of each case) also covered a copy with 2 MB of stack commit, frames from 0.4 KB, the other operations and the default number of JIT threads. With 2 MB every size up to 128 KB folds in every run with both engines.
exec,match,searchandreplacebehave astestdoes. With the three JIT threads that bun starts by default and one page, 1.6 KB folds in every run and nothing folds from 16 KB. The largest frame among 4,219 regexes of the packages intest/node_modulesis 5,984 bytes (measured for #45094).The engine without the fix folds some frames that the fix gives up: those that end in the guard region, where the kernel commits the stack on a write. oven-sh/WebKit#946 says why the limit is not lower.
The cost of a match that is not folded
jscof the WebKit branch on Linux x64, against a copy in which the fold is always refused. Instructions in user space, three runs each./^(a?){375}b$/.test("aaab"), 5.9 KB frame/^(a?){25}b$/.test("aaab"), 0.4 KB frame/^a+b$/.test("aaab")The second change in that build
On every platform, the RegExp interpreter reports a search that it gives up as no match, and the DFG could fold
test()tofalsefor a string that matches. With oven-sh/WebKit#946 a JIT thread folds a match only when the engine answered it. That pull request has three tests for it.Size
From the
bun-webkitbuilds, 8c6c963e3c against the preview:.textofjsc.exegrows by 1,280 bytes on Windows x64 and by 1,152 on arm64, and.textof the Linux x64 LTOjscby 768. The match functions of the thread that runs JavaScript have the same instructions in both builds, on Linux and in the Windows libraries.The size of bun itself is not measured: the build artifacts of CI (S3) could not be downloaded from the machine that wrote this.