Skip to content

[JSC] Stop forcing the generic typed array prototype functions inline - #465

Merged
Jarred-Sumner merged 1 commit into
mainfrom
claude/typedarray-proto-no-always-inline
Aug 18, 2026
Merged

[JSC] Stop forcing the generic typed array prototype functions inline#465
Jarred-Sumner merged 1 commit into
mainfrom
claude/typedarray-proto-no-always-inline

Conversation

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Follow-up to #403. Restoring ALWAYS_INLINE = __always_inline__ cost every Bun platform 2.5–4 MB of stripped binary (bun-linux-x64 +3.67 MB, bun-windows-x64 +4.04 MB, oven-sh/bun#37352). A per-function diff of .text between the two FreeBSD (non-LTO, unstripped) bun-profile binaries either side of the bump shows the single most concentrated cost is this header:

function before after
typedArrayViewProtoFuncSort 422 B 290,856 B
typedArrayViewProtoFuncToSorted 422 B 290,189 B
typedArrayViewProtoFuncFilter 422 B 76,952 B
typedArrayViewProtoFuncMap 422 B 51,685 B
typedArrayViewProtoFuncFind / FindIndex / FindLast / FindLastIndex 422 B ~40 KB each
typedArrayViewProtoFuncSome / Every / ForEach 422 B 34–39 KB
arrayProtoFuncSort / arrayProtoFuncToSorted 361 B / 1,009 B 64,657 B / 65,561 B

typedArrayViewProtoFuncSort and ToSorted are now the two largest functions in Bun's whole binary — bigger than sqlite3VdbeExec or DFG::ByteCodeParser::parseBlock.

Every genericTypedArrayViewProto*<ViewClass> is reached from the 12-way type switch in CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION, which tail-calls it and returns, so forcing them inline gains nothing: it just moves the twelve <ViewClass> instantiations (and for sort/toSorted, twelve copies of arrayStableSort + insertion sort + the comparator path) into one host function. This demotes the entry points and genericTypedArrayViewProtoFuncSortImpl to plain inline, so each instantiation is its own function again (as it was before #403). The helpers that specialise on a functor and are the actual hot loops — typedArrayViewForEachImpl, typedArrayIndexOfImpl, typedArrayLastIndexOfImpl — stay ALWAYS_INLINE.

Same change for ArrayPrototype.cpp's sortImpl, which was being duplicated into both arrayProtoFuncSort and arrayProtoFuncToSorted.

No behaviour change; only inlining hints. I will pin a Bun draft PR to this PR's preview tarball to get the per-platform size annotation.

Every genericTypedArrayViewProto*<ViewClass> is reached from a 12-way type
switch (CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION) that tail-calls it and
returns. ALWAYS_INLINE on the entry points gains nothing there and puts twelve
copies of each body (and, for sort/toSorted, twelve copies of the whole
stable-sort implementation) into one host function: typedArrayViewProtoFuncSort
and ToSorted are 290 KB each in a release JavaScriptCore, the two largest
functions in the binary. Demote the entry points and SortImpl to plain inline
so the per-ViewClass instantiations are separate functions again; the loop
helpers that specialise on a functor (typedArrayViewForEachImpl,
typedArrayIndexOfImpl, typedArrayLastIndexOfImpl) stay ALWAYS_INLINE.

Same for ArrayPrototype's sortImpl, which was being duplicated into both
arrayProtoFuncSort and arrayProtoFuncToSorted (~64 KB each).
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 12 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 74 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 07a4a8be-5295-4ac3-b440-7afb9e0f0521

📥 Commits

Reviewing files that changed from the base of the PR and between eeab040 and 8cc9fee.

📒 Files selected for processing (2)
  • Source/JavaScriptCore/runtime/ArrayPrototype.cpp
  • Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeFunctions.h

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

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — mechanical ALWAYS_INLINEinline demotion on function signatures only.

What was reviewed:

  • Confirmed every changed line touches only the inline specifier; no function body or logic changed.
  • Verified each genericTypedArrayViewProto* is reached via CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION in the JSTypedArrayViewPrototypeFunctions*.cpp dispatch shims, so weakening the hint doesn't strand any caller.
  • Checked that the hot-loop helpers (typedArrayViewForEachImpl, typedArrayIndexOfImpl, typedArrayLastIndexOfImpl) keep ALWAYS_INLINE, and sortImpl in ArrayPrototype.cpp stays static so linkage is unchanged.
Extended reasoning...

Overview

This PR changes 31 function signatures in JSGenericTypedArrayViewPrototypeFunctions.h and one in ArrayPrototype.cpp from ALWAYS_INLINE (i.e. __attribute__((always_inline))) to plain inline. Every hunk is a one-token substitution on the declarator line; no function body, control flow, or type is touched.

Security risks

None. This is a compiler inlining hint change only. It cannot alter observable JS semantics, memory safety, or bounds checking — the generated code is the same instructions, just partitioned into separate symbols instead of being force-inlined into the 12-way type-dispatch host function.

Level of scrutiny

Low. The diff is purely mechanical and the intent (recover ~3–4 MB of binary bloat introduced when #403 restored ALWAYS_INLINE's force semantics) is well-documented with per-function .text size measurements. I confirmed via grep that the affected templates are called only through CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION in the split JSTypedArrayViewPrototypeFunctions{1..5}.cpp shims — a switch that immediately returns the result — so dropping the force hint just lets each <ViewClass> instantiation stay a separate function rather than being copied 12× into one host body. The functor-specialised inner loops that actually matter for perf keep ALWAYS_INLINE.

Other factors

Templates defined in a header already have inline linkage, and sortImpl in the .cpp keeps static, so there is no ODR or linkage change. The only possible effect is codegen layout / binary size, which is exactly the point of the PR and will be validated by the pinned Bun draft PR's size annotations. No outstanding reviewer comments.

@Jarred-Sumner
Jarred-Sumner merged commit 8d11548 into main Aug 18, 2026
37 of 47 checks passed
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.

1 participant