Skip to content

jsc: apply BUN_JSC_ options without verifying each one - #40583

Open
robobun wants to merge 3 commits into
mainfrom
farm/5db47d3e/jsc-options-verify-once
Open

robobun wants to merge 3 commits into
mainfrom
farm/5db47d3e/jsc-options-verify-once

Conversation

@robobun

@robobun robobun commented Aug 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • BUN_JSC_jitPolicyScale=0.5 scaled the tier-up thresholds once per BUN_JSC_ variable in the environment, not once. With BUN_JSC_useConcurrentJIT=0 and BUN_JSC_dumpOptions=2 also set, thresholdForOptimizeAfterWarmUp came out as 125 instead of 500, and adding any other BUN_JSC_ variable changed it again.
  • JSCInitialize (src/jsc/bindings/ZigGlobalObject.cpp:364) called JSC::Options::setOption(env + 8) with the default verify = true. Every verified change runs Options::notifyOptionsChanged, and that pass multiplies the thresholds by jitPolicyScale whenever the scale was overridden (Options.cpp:908, scaleJITPolicy). Options::initialize then runs the same pass once more after the callback.

Fix

  • Set each option with verify = false, as jsc.cpp does for -- options, and let the single notifyOptionsChanged that Options::initialize runs after the callback verify them all. The scale is applied exactly once.
  • Drop the assertOptionsAreCoherent() call at the end of the callback. It ran before the pass that makes the options coherent, so with verify = false it would crash BUN_JSC_useProfiler=1 at startup (the pass turns the concurrent JIT off for the profiler). Options::finalize runs the same assertion afterwards.
  • Verified: bun bd test test/cli/run/env.test.ts (100 pass). The new test fails on the released binary (thresholdForOptimizeAfterWarmUp=125) and passes with the fix (500). test/js/node/buffer-jit.test.ts from test(buffer-jit): speed up buffer-jit.test.ts and pin exact compile counts #40542 passes against the fixed binary.

Background

  • JSC options are process-wide settings read from BUN_JSC_<name> environment variables at startup. Some options are derived from others: jitPolicyScale scales seven threshold options, useProfiler turns useConcurrentJIT off. notifyOptionsChanged computes those derived values.
  • Options::initialize takes a customization callback (Bun's lambda in JSCInitialize), runs it, then runs notifyOptionsChanged once. setOption(arg, verify) runs the same pass after each option when verify is true, which is meant for options set at runtime, after initialization.
  • Found while working on test(buffer-jit): speed up buffer-jit.test.ts and pin exact compile counts #40542: that test file used jitPolicyScale=0.05 and its effective thresholds sat at the floor because the scale was applied two or three times.

no test proof · iteration 1 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/run/env.test.ts

Options::initialize runs notifyOptionsChanged once after the options
customization callback. Verifying every BUN_JSC_ option inside the
callback ran that pass once per variable, and each pass multiplied the
tier-up thresholds by jitPolicyScale again, so the scale depended on how
many BUN_JSC_ variables were set. Set the options without verification,
like jsc.cpp does, and drop the coherence assertion that ran before the
pass that makes the options coherent.
@coderabbitai

coderabbitai Bot commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 25 days. After that, they cost $0.25 per reviewed file.

Or wait 11 minutes for your next included review.

View limit details

Limit details: You’ve used all 5 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0ee16f3c-bfb4-4cdb-bc59-9a6e4b34f631

📥 Commits

Reviewing files that changed from the base of the PR and between fb65775 and 4b7565e.

📒 Files selected for processing (2)
  • src/jsc/bindings/ZigGlobalObject.cpp
  • test/cli/run/env.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Aug 26, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: one-line runtime change plus two tests in test/cli/run/env.test.ts. The new jitPolicyScale test fails on the released binary (threshold 125) and passes with the fix (500).

CI (build 106447): env.test.ts passes on all 10 lanes that ran it. The red lanes are not this diff: bun-build-api.test.ts is the chunk-hash snapshot break from #40518 (fix in #40545), url.test.ts on macOS x64 and bun-patch.test.ts on Windows fail the same way without this change (reported to triage), and the Windows aarch64 build lane timed out downloading the Rust toolchain. Ready for review.

@robobun

robobun commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

Status: one-line runtime change plus two tests in test/cli/run/env.test.ts. The new jitPolicyScale test fails on the released binary (threshold 125) and passes with the fix (500). Waiting for CI.

Comment thread test/cli/run/env.test.ts Outdated
Comment thread src/jsc/bindings/ZigGlobalObject.cpp Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the follow-up commit reworked the useProfiler test to positively assert a derived option, which addresses my earlier note.

What was reviewed:

  • JSCInitialize: verify=false matches how jsc.cpp sets -- options; the dropped assertOptionsAreCoherent() is redundant with the one Options::finalize runs after notifyOptionsChanged.
  • The jitPolicyScale test fails on base (125) and passes with the fix (500), satisfying the USE_SYSTEM_BUN check; the second test guards the new invariant that the post-callback pass still runs.
  • Checked the $-anchored regexes against Windows CRLF from CRT stderr — ruled out.
Extended reasoning...

Overview

This PR changes two lines in JSCInitialize (src/jsc/bindings/ZigGlobalObject.cpp): the BUN_JSC_* env-var loop now calls JSC::Options::setOption(env + 8, /* verify */ false) instead of the default verify=true, and the trailing JSC::Options::assertOptionsAreCoherent() is removed. Two test.concurrent cases are appended to the existing describe("JSC option environment variables") block in test/cli/run/env.test.ts, reusing the dumpOptions helper already in that block.

Security risks

None. This is startup-time JSC option plumbing driven by process environment variables that were already being read and applied; the change only alters when the coherence/derivation pass runs (once after all options are set, rather than once per option). No new input surface, no auth/crypto/permissions.

Level of scrutiny

Low-to-moderate. The C++ change is two lines and mirrors the pattern WebKit's own jsc.cpp uses for -- command-line options. The rationale is well-documented: Options::initialize calls notifyOptionsChanged once after the customization callback returns, and Options::finalize asserts coherence afterwards, so per-option verification inside the callback is both redundant and actively wrong for jitPolicyScale (which scaleJITPolicy compounds on every pass). The removed assertOptionsAreCoherent() ran before the pass that makes options coherent, so with verify=false it would have crashed valid combinations like useProfiler=1 — removing it is required, not incidental.

Other factors

My earlier inline comment (that the original useProfiler test only asserted "didn't crash" and passed on base) was addressed in a17331c: the test now asserts needDisassemblySupport=true, a value the post-callback verification pass derives. The jitPolicyScale test is the one that fails on the released binary (=125) and passes with the fix (=500), covering the actual bug. Tests follow repo conventions: added to the existing file, test.concurrent, bunEnv spread in the shared helper, output asserted before exit code. The Windows /…$/m CRLF concern was investigated this run and ruled out. No CODEOWNERS entries cover these paths.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant