Skip to content

wasm: make reflection entries callable across workers - #2743

Merged
cpunion merged 7 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-reflect-worker-tables-20261006
Oct 14, 2026
Merged

cpunion merged 7 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-reflect-worker-tables-20261006

Conversation

@cpunion

@cpunion cpunion commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Follows merged #2738. reflect.MakeFunc goroutines trapped with “table index is out of bounds” because libffi installed a dynamic entry only in the creating worker’s table. JavaScript providers now use compiler-generated Wasm reflection entries by default, shared by every instance. Preserve reachability gating, signature deduplication and cache fingerprints.

Single-worker JS builds may opt into the previous libffi reflection backend with -tags=llgo.wasm.reflect.libffi. Restore its original FuncType descriptor and JS replay trampolines; enable their three Asyncify exclusions only for this tag. Reject the tag for multiple workers, explicit worker tags, WASI and native targets. Preserve the caller/GC exclusions, ffi_call_js async import and table-growth setting. Native reflection and C libffi retain their backends.

Validation: profile/group/cache and build-entry rejection tests; Node/Chrome Memory32/64 with 2/4 workers; complete test/go packages for worker Memory32/64 and libffi Emscripten Memory32/64 plus GoJS. Compiler selector/validation functions have 100% focused coverage. Full-package tests use a 180s alarm within the existing 300s process limit, allowing CI headroom for repeated collections. The allocator progress, bounded GC metadata scans and rooted filesystem buffers from #2738 are already in main. Remove the duplicate segmented-heap constant in the host GC fixture so both heap modes compile and remain covered.

The reflection sample’s single-worker Wasm decreases from 1,510,276 to 1,446,947 bytes with libffi; JS increases by 9,974 bytes. A default → libffi → default build/run sequence preserves results and the default Wasm size with the shared cache. Earlier default-bridge measurements grew large test/go from about 15.75 MB to 18.20 MB; further bridge/metadata optimization is tracked in #2679.

Rebased on main 45bdf2db2, which includes #2738 and #2742.

@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai please review the reflection follow-up range 87995c1..958f455, especially worker-local Wasm table correctness, typed bridge reachability/cache consistency and native backend isolation. Full std qualification reproduced the old backend trap on both Memory32/64. Existing reflection/defer/recover tests and compiler/cache tests pass with the fix; the new Node/Chrome worker fixture is running locally. The PR records the measured module size cost rather than claiming this backend change reduces size.

@fennoai fennoai 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.

Review summary

This PR reworks WASM Emscripten worker mode so syscall/js values are shared across workers (all remote JS ops forward to worker zero via a per-caller executor G), and switches Emscripten reflection from libffi to compiler-generated typed bridges so MakeFunc values survive crossing workers. The engineering is careful and well-commented: build-tag partitioning is mutually exclusive and exhaustive, the executor reuse / //go:noinline pattern is deliberate, the runqueue Remove/RemoveAfter/Front additions and the finalizer-mutex refactor are correct, and the bilingual proposal/doc updates match the code with no stale jsRealm/MarkCurrentJSRealm/libffi-reflection references remaining.

Findings below. The scheduler items (reentrancy + O(n) child scan) are the ones worth a close look; the rest are minor.

Body-level finding (location not inline-commentable):

Possible reentrant goschedBackend while a G is mid status-transition. goschedBackend (runtime/internal/runtime/proc_wasm_workers.go:399-408) transitions gp to _Grunnable via casgstatus(gp, _Grunning, _Grunnable) (line 402) before acquiring the worker lock in enqueueWasmG (line 403 → worker.lock.Lock(CooperativeSafepoint) at 449). On a contended lock, wasmsync.Mutex.Lock invokes the yield (CooperativeSafepoint). If the safepoint budget is exhausted and no GC stop is pending, cooperativeSafepointSlow (runtime/internal/runtime/safepoint_wasm_workers.go:63-65, not in this diff) calls goschedBackend() again when the runq is non-empty, re-running casgstatus(gp, _Grunning, _Grunnable) on a G that is already _Grunnable — a failed transition that would fatal. goready (line 431-433) has the same shape. This is narrow (needs same-worker lock contention + budget expiry at that exact poll + non-empty runq) and may be unreachable for reasons I can't fully confirm without running the scheduler, but since this PR makes cross-worker enqueues (and thus lock contention) much more frequent, it's worth confirming the queue-lock yield cannot reenter goschedBackend while a caller holds gp in a transient status.

@fennoai

fennoai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Reflection follow-up review — 87995c126..958f4552a

Focused on the three areas you called out. The backend swap (libffi JS trampolines → compiler-generated typed bridges for all GOOS=js providers) is coherent and the test matrix changes correctly pin the new behavior. One concrete cleanup finding in the native-backend-isolation area, plus a cosmetic consistency nit.

Finding — stale Emscripten reflection linker flags (native backend isolation)

internal/crosscompile/crosscompile.go still passes libffi-reflection glue that this commit removed for the js profile:

  • emscriptenAsyncifyRemove (line 129, applied at line 886):
    -sASYNCIFY_REMOVE=["*llgo_reflect_bind0_js*","*llgo_reflect_bind1_js*","*llgo_reflect_bindn_js*"]
    Those three C trampolines were defined only in the now-deleted runtime/internal/lib/reflect/_wrap/makefunc_wasm_js.c. They no longer exist in any js build, so these ASYNCIFY_REMOVE patterns now match nothing.
  • emscriptenAsyncifyImports (line ~124): still lists ffi_call_js, described in its comment as the import needed so "Asyncify cannot unwind a sleeping Go function invoked by reflect.Value.Call / MakeFunc." That path is now the typed bridge (callWasmBridge), which doesn't route reflection through ffi_call_js (no definition/use of that symbol remains in-tree).

Impact is low — unmatched ASYNCIFY_REMOVE wildcards are effectively a no-op and a spurious async import just keeps an unused symbol unwindable — so this won't break the build. But both flags, and their adjacent comments (lines 122–128), now describe a reflection mechanism that no longer applies to this profile, which is misleading for the next person touching the Emscripten link step. Worth removing the reflection-trampoline entries (and trimming ffi_call_js from imports if it's only reflection-related) or re-scoping the comments to the remaining C-integration libffi use. Note appendEmscriptenLibffiSearchPath and the C libffi path are still legitimately needed, so libffi itself should stay.

Nit — predicate form differs across the three gates (cache consistency)

The reflect-bridge gate is now spelled three ways: target.GOOS == "js" in initial_groups.go:46 and wasm_reflect.go:43 (raw field) vs p.effectiveGOOS() == "js" in ssa/target.go:62 (host-GOOS fallback when empty). For wasm builds GOOS is always set explicitly, so they classify identically today and the group cache key stays consistent with what's emitted — but aligning all three on the same helper would remove a latent way for the build-group key and the actual bridge emission to diverge.

Verified consistent (no action)

  • Group dedup key (initial_groups.go) agrees with configureWasmReflectBridges; reachability gating via usesWasmReflectBridges() is preserved, so dead-reflection std helpers still emit nothing per build.
  • Build-tag pairs for functype_*, call_bridge_*, makefunc_ffi.go are exact complements (wasip1 || js vs !wasip1 && !js); no js-profile file still references the deleted bind0JS/bind1JS/bindnJS/invokeJSMakeFunc symbols.
  • Worker-local-table rationale is sound, and the new test/wasm/reflect-workers/main.go fixture has no build constraint, so it exercises the typed-bridge path under single-worker js and WASI too, not only worker mode — good coverage for the widened gate.
  • Test matrices (wasm_reflect_test.go, reflect_bridge_test.go) correctly flip GoJS J32 to typed bridges and add active Emscripten J32/J64 cases.

Thanks for the context on the reproduced old-backend trap and the module-size-cost recording — that matches what the proposal text now claims (measured cost, not a size-reduction claim).

@cpunion
cpunion force-pushed the codex/wasm-reflect-worker-tables-20261006 branch from 958f455 to f54fe33 Compare October 6, 2026 05:33
@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

The body-level runqueue finding was confirmed with a controlled contended-lock/budget-exhaustion probe: the preemptive callback fails with invalid goroutine status transition; the in-place GC callback passes. Parent #2738 now uses the existing in-place GC wait at all three queue-lock sites (cc47f7a), with a regression exercising actual Gosched under contention both without and with concurrent GC. This PR is rebased onto that fix. The reflection changes also remove the obsolete Asyncify trampoline exclusions; C libffi imports remain supported.

@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai please review only the reflection follow-up range cc47f7a..f54fe33; cc47f7a is the parent #2738 and contains the scheduler fixes discussed above. The follow-up replaces worker-local dynamic reflection entries with shared static ones, keeps reachability/cache gating, and removes obsolete reflection-only Asyncify exclusions. Full local Node/Chrome 2/4-worker Memory32/64 acceptance passes; fresh std qualification is pinned to the reflection fix at 958f455. Native/C libffi backends remain supported. The outstanding scan-complexity discussion belongs to the parent callback scheduler and is recorded as performance qualification rather than changed by this reflection patch.

@fennoai

fennoai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Reflection follow-up review — cc47f7a1f..f54fe33ba

Reviewed the two-commit range (b67824efe compiled reflection entries, reparented onto #2738; f54fe33ba Asyncify-exclusion cleanup). The stale-flags finding from the previous round is fully resolved, and no new issues.

Resolved — obsolete reflection-only Asyncify exclusions removed ✓

f54fe33ba addresses exactly what I flagged last round:

  • Drops the emscriptenAsyncifyRemove constant and its LDFLAG — the *llgo_reflect_bind0_js*/bind1/bindn patterns pointed at trampolines that lived only in the deleted _wrap/makefunc_wasm_js.c, so they matched nothing.
  • Removes the now-dead crosscompile_test.go assertion for that flag.
  • Re-scopes the ffi_call_js comment to "the C libffi API. Go reflection uses static typed entries" — correctly separating the retained C-integration use from the removed reflection use.

Verified:

  • No dangling references anywhere to the removed trampoline symbols or ASYNCIFY_REMOVE (grep across .go/.c is clean).
  • ffi_call_js correctly retained in ASYNCIFY_IMPORTS — libffi's ffi_call is still used by the C path (runtime/internal/clite/ffi/_wrap/libffi.c:280), so the native/C backend stays unwindable. Matches your note that native/C libffi backends remain supported.
  • Changed Go files are gofmt-clean, including the realigned const block after removing the longest-named constant.

Unchanged — reflection-entries commit matches prior-reviewed content ✓

b67824efe is the same 16-file change I reviewed earlier (shared static typed bridges for all GOOS=js providers, reachability/cache gating preserved, build-tag complements exact, reflect-workers fixture unconstrained so it also exercises single-worker js + WASI). No new concerns.

Nit (carryover, non-blocking)

The reflect-bridge gate is still spelled target.GOOS == "js" in initial_groups.go:46 + wasm_reflect.go:43 but p.effectiveGOOS() == "js" in ssa/target.go:62. Identical in practice (wasm always sets GOOS explicitly), so the group cache key stays consistent with emitted bridges — purely a style-alignment opportunity, not a defect.

Noted and agreed that the callback-scheduler scan-complexity item belongs to the parent (#2738) and is tracked as performance qualification, not something this reflection patch changes.

@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

LLGo WebAssembly build benchmarks

6fd8e060243e | workflow run | long-term charts

WebAssembly output sizes
Example, profile and compiler Wasm module vs base Generated JS glue vs base
cprintf/j32-emscripten/LLGo 147058 B +1 B / +0.00068% (worse) 88541 B 0 B / +0.0%
cprintf/j32-goos-js/LLGo 145666 B 0 B / +0.0% 72985 B 0 B / +0.0%
cprintf/j64-emscripten-memory64/LLGo 134527 B +1 B / +0.0007434% (worse) 91712 B 0 B / +0.0%
cprintf/w32-goos-wasip1/LLGo 156385 B 0 B / +0.0% 0 B 0 B / 0.0%
cprintf/w32-wasi/LLGo 157083 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-emscripten/LLGo 2983755 B -19910 B / -0.7% (better) 120989 B -10058 B / -7.7% (better)
fmtprintf/j32-goos-js/Go 2526852 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-goos-js/LLGo 3074524 B -19538 B / -0.6% (better) 92052 B -10058 B / -9.9% (better)
fmtprintf/j64-emscripten-memory64/LLGo 2748599 B -17412 B / -0.6% (better) 125530 B -10997 B / -8.1% (better)
fmtprintf/w32-goos-wasip1/Go 2500019 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-goos-wasip1/LLGo 2343602 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-wasi/LLGo 2340298 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-emscripten/LLGo 146575 B +1 B / +0.0006822% (worse) 88541 B 0 B / +0.0%
j32-goos-js/Go 1895533 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-goos-js/LLGo 145416 B +2 B / +0.001375% (worse) 72985 B 0 B / +0.0%
j64-emscripten-memory64/LLGo 134082 B -1 B / -0.0007458% (better) 91712 B 0 B / +0.0%
reflectcall/j32-emscripten/LLGo 1509910 B +63325 B / +4.4% (worse) 95329 B -9974 B / -9.5% (better)
reflectcall/j32-goos-js/Go 2191221 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/j32-goos-js/LLGo 1562077 B +66647 B / +4.5% (worse) 79769 B -9974 B / -11.1% (better)
reflectcall/j64-emscripten-memory64/LLGo 1391021 B +52103 B / +3.9% (worse) 99026 B -10856 B / -9.9% (better)
reflectcall/w32-goos-wasip1/Go 2205707 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-goos-wasip1/LLGo 1273789 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-wasi/LLGo 1271430 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/Go 1909947 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/LLGo 156059 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-wasi/LLGo 156757 B 0 B / +0.0% 0 B 0 B / 0.0%
LLGo WebAssembly build measurements
Example and profile Build vs base
j32-emscripten 7.319 s -186.1 ms / -2.5% (better)
j32-goos-js 7.281 s +200.9 ms / +2.8% (worse)
j64-emscripten-memory64 6.227 s -51.66 ms / -0.8% (better)
reflectcall/w32-wasi 24.195 s -252.3 ms / -1.0% (better)
w32-goos-wasip1 4.390 s -27 ms / -0.6% (better)
w32-wasi 4.202 s +20.38 ms / +0.5% (worse)

Compared with 45bdf2db295a measured in the same runner job.

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

LLGo baseline benchmarks

6fd8e060243e | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Text size vs base Build vs base Run vs base
Linux cprintf 8568 B 0 B / +0.0% 387 B 0 B / +0.0% 937.721 ms -74.18 us / -0.00791% (better) 1.378 ms +14.98 us / +1.1% (worse)
Linux cprintf-lto 8408 B 0 B / +0.0% 368 B 0 B / +0.0% 911.214 ms -18.32 ms / -2.0% (better) 1.357 ms -92.47 us / -6.4% (better)
Linux fmtprintf 4915576 B 0 B / +0.0% 506206 B 0 B / +0.0% 6.029 s -18.62 ms / -0.3% (better) 4.498 ms +239.7 us / +5.6% (worse)
Linux fmtprintf-lto 3621616 B 0 B / +0.0% 444290 B 0 B / +0.0% 15.315 s -112.5 ms / -0.7% (better) 4.175 ms +165.1 us / +4.1% (worse)
Linux println 705080 B 0 B / +0.0% 17095 B 0 B / +0.0% 915.373 ms -13.61 ms / -1.5% (better) 1.809 ms +9.917 us / +0.6% (worse)
Linux println-lto 192928 B 0 B / +0.0% 14484 B 0 B / +0.0% 1.238 s -31.69 ms / -2.5% (better) 1.736 ms -108.5 us / -5.9% (better)
macOS cprintf 50736 B 0 B / +0.0% 4409 B 0 B / +0.0% 1.242 s +173 ms / +16.2% (worse) 3.898 ms +1.049 ms / +36.8% (worse)
macOS cprintf-lto 50496 B 0 B / +0.0% 161 B 0 B / +0.0% 1.120 s +106.4 ms / +10.5% (worse) 3.709 ms +886.7 us / +31.4% (worse)
macOS fmtprintf 1793088 B 0 B / +0.0% 883744 B 0 B / +0.0% 5.111 s +227.3 ms / +4.7% (worse) 65.278 ms +24.32 ms / +59.4% (worse)
macOS fmtprintf-lto 1362176 B 0 B / +0.0% 767588 B 0 B / +0.0% 13.477 s +2.45 s / +22.2% (worse) 135.189 ms +76.04 ms / +128.5% (worse)
macOS println 100080 B 0 B / +0.0% 24519 B 0 B / +0.0% 1.111 s +23.32 ms / +2.1% (worse) 7.590 ms +2.046 ms / +36.9% (worse)
macOS println-lto 84016 B 0 B / +0.0% 21760 B 0 B / +0.0% 1.335 s -165.8 ms / -11.0% (better) 5.123 ms +589 us / +13.0% (worse)
Windows MinGW cprintf 676352 B 0 B / +0.0% 4550 B 0 B / +0.0% 1.222 s -9.632 ms / -0.8% (better) 2.780 ms +15.8 us / +0.6% (worse)
Windows MinGW cprintf-lto 44544 B 0 B / +0.0% 4502 B 0 B / +0.0% 1.268 s +24.23 ms / +1.9% (worse) 2.776 ms +33.8 us / +1.2% (worse)
Windows MinGW fmtprintf 5473792 B 0 B / +0.0% 606134 B 0 B / +0.0% 4.142 s -21.75 ms / -0.5% (better) 8.408 ms +68.7 us / +0.8% (worse)
Windows MinGW fmtprintf-lto 4161024 B 0 B / +0.0% 553254 B 0 B / +0.0% 10.399 s +110.1 ms / +1.1% (worse) 8.232 ms +1.3 us / +0.01579% (worse)
Windows MinGW println 733696 B 0 B / +0.0% 25446 B 0 B / +0.0% 1.246 s +25.84 ms / +2.1% (worse) 5.009 ms -47.7 us / -0.9% (better)
Windows MinGW println-lto 210944 B 0 B / +0.0% 22326 B 0 B / +0.0% 1.453 s -6.63 ms / -0.5% (better) 4.943 ms -146 us / -2.9% (better)
Windows MinGW 386 cprintf 627200 B 0 B / +0.0% 5352 B 0 B / +0.0% 1.263 s +26.42 ms / +2.1% (worse) 3.889 ms -71.1 us / -1.8% (better)
Windows MinGW 386 cprintf-lto 104448 B 0 B / +0.0% 5112 B 0 B / +0.0% 1.281 s -18.38 ms / -1.4% (better) 3.878 ms -79.8 us / -2.0% (better)
Windows MinGW 386 fmtprintf 4776960 B 0 B / +0.0% 473902 B 0 B / +0.0% 4.539 s +311.6 ms / +7.4% (worse) 11.498 ms +1.837 ms / +19.0% (worse)
Windows MinGW 386 fmtprintf-lto 4163072 B 0 B / +0.0% 452294 B 0 B / +0.0% 10.988 s +1.046 s / +10.5% (worse) 12.291 ms +2.332 ms / +23.4% (worse)
Windows MinGW 386 println 681472 B 0 B / +0.0% 21694 B 0 B / +0.0% 1.280 s +2.251 ms / +0.2% (worse) 6.817 ms +51.7 us / +0.8% (worse)
Windows MinGW 386 println-lto 260608 B 0 B / +0.0% 19514 B 0 B / +0.0% 1.513 s +28.95 ms / +2.0% (worse) 6.552 ms +253.3 us / +4.0% (worse)
Windows MinGW ARM64 cprintf 686080 B 0 B / +0.0% 4436 B 0 B / +0.0% 1.745 s -45.64 ms / -2.5% (better) 5.845 ms -208.8 us / -3.4% (better)
Windows MinGW ARM64 cprintf-lto 44032 B 0 B / +0.0% 4388 B 0 B / +0.0% 1.787 s -25.33 ms / -1.4% (better) 5.837 ms +16.4 us / +0.3% (worse)
Windows MinGW ARM64 fmtprintf 5382656 B 0 B / +0.0% 514292 B 0 B / +0.0% 5.051 s -107.7 ms / -2.1% (better) 13.377 ms -1.06 ms / -7.3% (better)
Windows MinGW ARM64 fmtprintf-lto 4327424 B 0 B / +0.0% 480352 B 0 B / +0.0% 11.295 s -167.1 ms / -1.5% (better) 12.825 ms -365.3 us / -2.8% (better)
Windows MinGW ARM64 println 739840 B 0 B / +0.0% 24160 B 0 B / +0.0% 1.747 s -30.72 ms / -1.7% (better) 9.897 ms -124.9 us / -1.2% (better)
Windows MinGW ARM64 println-lto 218624 B 0 B / +0.0% 21516 B 0 B / +0.0% 1.995 s -39.35 ms / -1.9% (better) 9.499 ms -480.1 us / -4.8% (better)
Windows MSVC cprintf 921088 B 0 B / +0.0% 66003 B 0 B / +0.0% 1.413 s -14.82 ms / -1.0% (better) 3.394 ms +9.9 us / +0.3% (worse)
Windows MSVC cprintf-lto 292352 B 0 B / +0.0% 65971 B 0 B / +0.0% 1.430 s -13.4 ms / -0.9% (better) 3.412 ms -46.9 us / -1.4% (better)
Windows MSVC fmtprintf 5774848 B 0 B / +0.0% 701894 B 0 B / +0.0% 4.992 s -59.34 ms / -1.2% (better) 11.595 ms -420.5 us / -3.5% (better)
Windows MSVC fmtprintf-lto 4484096 B 0 B / +0.0% 652806 B 0 B / +0.0% 11.583 s -151.7 ms / -1.3% (better) 12.689 ms +315.9 us / +2.6% (worse)
Windows MSVC println 1044992 B 0 B / +0.0% 121334 B 0 B / +0.0% 1.414 s -26.95 ms / -1.9% (better) 8.030 ms +468 us / +6.2% (worse)
Windows MSVC println-lto 531456 B 0 B / +0.0% 118870 B 0 B / +0.0% 1.645 s -36.74 ms / -2.2% (better) 7.427 ms +487.5 us / +7.0% (worse)
Windows MSVC 386 cprintf 538624 B 0 B / +0.0% 3947 B 0 B / +0.0% 1.069 s -33.17 ms / -3.0% (better) 4.441 ms +300 ns / +0.006755% (worse)
Windows MSVC 386 cprintf-lto 44544 B 0 B / +0.0% 3901 B 0 B / +0.0% 1.089 s +5.794 ms / +0.5% (worse) 4.511 ms +99.6 us / +2.3% (worse)
Windows MSVC 386 fmtprintf 4508672 B 0 B / +0.0% 457314 B 0 B / +0.0% 3.963 s -79.08 ms / -2.0% (better) 10.193 ms -104.2 us / -1.0% (better)
Windows MSVC 386 fmtprintf-lto 3910656 B 0 B / +0.0% 427681 B 0 B / +0.0% 9.440 s +298 ms / +3.3% (worse) 10.604 ms +69.7 us / +0.7% (worse)
Windows MSVC 386 println 593920 B 0 B / +0.0% 20566 B 0 B / +0.0% 1.071 s -3.67 ms / -0.3% (better) 8.265 ms +1.188 ms / +16.8% (worse)
Windows MSVC 386 println-lto 200192 B 0 B / +0.0% 18711 B 0 B / +0.0% 1.288 s +29.51 ms / +2.3% (worse) 7.145 ms -158.3 us / -2.2% (better)
Windows MSVC ARM64 cprintf 687616 B 0 B / +0.0% 4240 B 0 B / +0.0% 1.615 s +92.88 ms / +6.1% (worse) 7.166 ms +470.6 us / +7.0% (worse)
Windows MSVC ARM64 cprintf-lto 49152 B 0 B / +0.0% 4140 B 0 B / +0.0% 1.619 s +84.54 ms / +5.5% (worse) 7.238 ms +789.9 us / +12.3% (worse)
Windows MSVC ARM64 fmtprintf 5379072 B 0 B / +0.0% 514164 B 0 B / +0.0% 5.107 s +224 ms / +4.6% (worse) 17.089 ms +748.8 us / +4.6% (worse)
Windows MSVC ARM64 fmtprintf-lto 4334080 B 0 B / +0.0% 480960 B 0 B / +0.0% 11.473 s +401.6 ms / +3.6% (worse) 17.905 ms +1.358 ms / +8.2% (worse)
Windows MSVC ARM64 println 741376 B 0 B / +0.0% 24200 B 0 B / +0.0% 1.610 s +93.48 ms / +6.2% (worse) 12.930 ms +1.287 ms / +11.1% (worse)
Windows MSVC ARM64 println-lto 223232 B 0 B / +0.0% 21688 B 0 B / +0.0% 1.831 s +41.6 ms / +2.3% (worse) 12.683 ms +1.044 ms / +9.0% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.750 ns/op +0.05 ns/op / +0.3% (worse)
Linux BenchmarkMergeCompilerFlags 196.400 ns/op -0.1 ns/op / -0.1% (better)
Linux BenchmarkMergeLinkerFlags 134.400 ns/op -0.2 ns/op / -0.1% (better)
Linux BenchmarkChannelBuffered 55.130 ns/op -0.11 ns/op / -0.2% (better)
Linux BenchmarkChannelHandoff 12944 ns/op -49 ns/op / -0.4% (better)
Linux BenchmarkDefer 47.600 ns/op -0.27 ns/op / -0.6% (better)
Linux BenchmarkDirectCall 1.164 ns/op -0.001 ns/op / -0.1% (better)
Linux BenchmarkGlobalRead 1.171 ns/op +0.002 ns/op / +0.2% (worse)
Linux BenchmarkGlobalWrite 7.765 ns/op +0.007 ns/op / +0.1% (worse)
Linux BenchmarkGoroutine 26230 ns/op +362 ns/op / +1.4% (worse)
Linux BenchmarkInterfaceCall 5.867 ns/op -0.035 ns/op / -0.6% (better)
Linux BenchmarkRuntimeGetG 2.759 ns/op -0.009 ns/op / -0.3% (better)
macOS BenchmarkLookupPCRandom 17.110 ns/op +0.47 ns/op / +2.8% (worse)
macOS BenchmarkMergeCompilerFlags 151.100 ns/op -7.1 ns/op / -4.5% (better)
macOS BenchmarkMergeLinkerFlags 111 ns/op +17.38 ns/op / +18.6% (worse)
macOS BenchmarkChannelBuffered 38.050 ns/op +2.93 ns/op / +8.3% (worse)
macOS BenchmarkChannelHandoff 8257 ns/op -3173 ns/op / -27.8% (better)
macOS BenchmarkDefer 50.850 ns/op +1.4 ns/op / +2.8% (worse)
macOS BenchmarkDirectCall 1.342 ns/op -0.082 ns/op / -5.8% (better)
macOS BenchmarkGlobalRead 1.272 ns/op -0.096 ns/op / -7.0% (better)
macOS BenchmarkGlobalWrite 1.708 ns/op +0.132 ns/op / +8.4% (worse)
macOS BenchmarkGoroutine 60036 ns/op -16436 ns/op / -21.5% (better)
macOS BenchmarkInterfaceCall 5.368 ns/op +0.282 ns/op / +5.5% (worse)
macOS BenchmarkRuntimeGetG 3.042 ns/op +0.261 ns/op / +9.4% (worse)
Windows MinGW BenchmarkLookupPCRandom 9.698 ns/op +0.121 ns/op / +1.3% (worse)
Windows MinGW BenchmarkMergeCompilerFlags 370.200 ns/op -4.5 ns/op / -1.2% (better)
Windows MinGW BenchmarkMergeLinkerFlags 358.100 ns/op +22.9 ns/op / +6.8% (worse)
Windows MinGW BenchmarkChannelBuffered 23.600 ns/op -0.03 ns/op / -0.1% (better)
Windows MinGW BenchmarkChannelHandoff 1109 ns/op -38 ns/op / -3.3% (better)
Windows MinGW BenchmarkDefer 41.410 ns/op +0.18 ns/op / +0.4% (worse)
Windows MinGW BenchmarkDirectCall 1.357 ns/op 0 ns/op / +0.0%
Windows MinGW BenchmarkGlobalRead 1.358 ns/op +0.002 ns/op / +0.1% (worse)
Windows MinGW BenchmarkGlobalWrite 2.163 ns/op -0.001 ns/op / -0.04621% (better)
Windows MinGW BenchmarkGoroutine 58010 ns/op +147 ns/op / +0.3% (worse)
Windows MinGW BenchmarkInterfaceCall 6.572 ns/op -0.066 ns/op / -1.0% (better)
Windows MinGW BenchmarkRuntimeGetG 1.631 ns/op -0.001 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkLookupPCRandom 21.590 ns/op +0.02 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkMergeCompilerFlags 555.200 ns/op +5.7 ns/op / +1.0% (worse)
Windows MinGW 386 BenchmarkMergeLinkerFlags 528.300 ns/op +7.5 ns/op / +1.4% (worse)
Windows MinGW 386 BenchmarkChannelBuffered 33.870 ns/op -0.03 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkChannelHandoff 726.600 ns/op -25 ns/op / -3.3% (better)
Windows MinGW 386 BenchmarkDefer 34.920 ns/op +0.41 ns/op / +1.2% (worse)
Windows MinGW 386 BenchmarkDirectCall 1.358 ns/op +0.001 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkGlobalRead 1.357 ns/op 0 ns/op / +0.0%
Windows MinGW 386 BenchmarkGlobalWrite 6.988 ns/op +0.005 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkGoroutine 71081 ns/op +1841 ns/op / +2.7% (worse)
Windows MinGW 386 BenchmarkInterfaceCall 7.351 ns/op +0.017 ns/op / +0.2% (worse)
Windows MinGW 386 BenchmarkRuntimeGetG 1.903 ns/op +0.003 ns/op / +0.2% (worse)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.100 ns/op +0.04 ns/op / +0.3% (worse)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 568.500 ns/op +11.4 ns/op / +2.0% (worse)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 524.600 ns/op -11.4 ns/op / -2.1% (better)
Windows MinGW ARM64 BenchmarkChannelBuffered 37.750 ns/op -0.75 ns/op / -1.9% (better)
Windows MinGW ARM64 BenchmarkChannelHandoff 5150 ns/op -122 ns/op / -2.3% (better)
Windows MinGW ARM64 BenchmarkDefer 49.510 ns/op -1.45 ns/op / -2.8% (better)
Windows MinGW ARM64 BenchmarkDirectCall 0.590 ns/op +0.0002 ns/op / +0.0339% (worse)
Windows MinGW ARM64 BenchmarkGlobalRead 0.593 ns/op +0.0001 ns/op / +0.01686% (worse)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.663 ns/op +0.0001 ns/op / +0.01508% (worse)
Windows MinGW ARM64 BenchmarkGoroutine 56529 ns/op -5966 ns/op / -9.5% (better)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.135 ns/op -0.009 ns/op / -0.2% (better)
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.806 ns/op -0.001 ns/op / -0.1% (better)
Windows MSVC BenchmarkLookupPCRandom 11.030 ns/op -0.06 ns/op / -0.5% (better)
Windows MSVC BenchmarkMergeCompilerFlags 618 ns/op -0.2 ns/op / -0.03235% (better)
Windows MSVC BenchmarkMergeLinkerFlags 540.300 ns/op -5.7 ns/op / -1.0% (better)
Windows MSVC BenchmarkChannelBuffered 40.620 ns/op -0.11 ns/op / -0.3% (better)
Windows MSVC BenchmarkChannelHandoff 1525 ns/op -349 ns/op / -18.6% (better)
Windows MSVC BenchmarkDefer 56.110 ns/op -1.14 ns/op / -2.0% (better)
Windows MSVC BenchmarkDirectCall 0.865 ns/op -0.0019 ns/op / -0.2% (better)
Windows MSVC BenchmarkGlobalRead 0.867 ns/op -0.0015 ns/op / -0.2% (better)
Windows MSVC BenchmarkGlobalWrite 6.056 ns/op -0.01 ns/op / -0.2% (better)
Windows MSVC BenchmarkGoroutine 76868 ns/op +4267 ns/op / +5.9% (worse)
Windows MSVC BenchmarkInterfaceCall 5.629 ns/op -0.034 ns/op / -0.6% (better)
Windows MSVC BenchmarkRuntimeGetG 2.153 ns/op +0.007 ns/op / +0.3% (worse)
Windows MSVC 386 BenchmarkLookupPCRandom 21.520 ns/op -0.08 ns/op / -0.4% (better)
Windows MSVC 386 BenchmarkMergeCompilerFlags 574 ns/op +24.1 ns/op / +4.4% (worse)
Windows MSVC 386 BenchmarkMergeLinkerFlags 519.600 ns/op -9 ns/op / -1.7% (better)
Windows MSVC 386 BenchmarkChannelBuffered 33.890 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkChannelHandoff 907.100 ns/op +42.5 ns/op / +4.9% (worse)
Windows MSVC 386 BenchmarkDefer 38.060 ns/op -0.17 ns/op / -0.4% (better)
Windows MSVC 386 BenchmarkDirectCall 1.357 ns/op -0.001 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkGlobalRead 1.358 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkGlobalWrite 6.977 ns/op -0.01 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkGoroutine 74517 ns/op +2684 ns/op / +3.7% (worse)
Windows MSVC 386 BenchmarkInterfaceCall 7.332 ns/op -0.004 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkRuntimeGetG 1.728 ns/op -0.175 ns/op / -9.2% (better)
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.100 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 573.100 ns/op -0.7 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 529.300 ns/op -10 ns/op / -1.9% (better)
Windows MSVC ARM64 BenchmarkChannelBuffered 37 ns/op +0.09 ns/op / +0.2% (worse)
Windows MSVC ARM64 BenchmarkChannelHandoff 2565 ns/op -201 ns/op / -7.3% (better)
Windows MSVC ARM64 BenchmarkDefer 63.890 ns/op +1.38 ns/op / +2.2% (worse)
Windows MSVC ARM64 BenchmarkDirectCall 0.590 ns/op -0.0002 ns/op / -0.03392% (better)
Windows MSVC ARM64 BenchmarkGlobalRead 0.590 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkGlobalWrite 3.761 ns/op +0.005 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGoroutine 61550 ns/op +1388 ns/op / +2.3% (worse)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.137 ns/op -0.005 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.768 ns/op -0.001 ns/op / -0.1% (better)
Timer runtime benchmarks
Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 919 ns/op +9.9 ns/op / +1.1% (worse)
Linux AfterFuncZeroDelivery/LLGo 36934 ns/op -1804 ns/op / -4.7% (better)
Linux CreateStop/Go 308.100 ns/op +4.8 ns/op / +1.6% (worse)
Linux CreateStop/LLGo 1926 ns/op +60 ns/op / +3.2% (worse)
Linux RearmStopped/Go 114.500 ns/op -3.3 ns/op / -2.8% (better)
Linux RearmStopped/LLGo 1398 ns/op +60 ns/op / +4.5% (worse)
Linux ResetActive/Go 67.390 ns/op -1.98 ns/op / -2.9% (better)
Linux ResetActive/LLGo 775.900 ns/op -153.1 ns/op / -16.5% (better)
Linux ResetHeap1024/Go 67.040 ns/op 0 ns/op / +0.0%
Linux ResetHeap1024/LLGo 158.200 ns/op -0.9 ns/op / -0.6% (better)
macOS AfterFuncZeroDelivery/Go 550.300 ns/op -65.7 ns/op / -10.7% (better)
macOS AfterFuncZeroDelivery/LLGo 101152 ns/op -11062 ns/op / -9.9% (better)
macOS CreateStop/Go 193.600 ns/op -0.9 ns/op / -0.5% (better)
macOS CreateStop/LLGo 549.500 ns/op +39.3 ns/op / +7.7% (worse)
macOS RearmStopped/Go 69.950 ns/op -5.18 ns/op / -6.9% (better)
macOS RearmStopped/LLGo 305.800 ns/op -119 ns/op / -28.0% (better)
macOS ResetActive/Go 51.990 ns/op -7.24 ns/op / -12.2% (better)
macOS ResetActive/LLGo 145.300 ns/op -28.8 ns/op / -16.5% (better)
macOS ResetHeap1024/Go 49.460 ns/op -5.05 ns/op / -9.3% (better)
macOS ResetHeap1024/LLGo 87.500 ns/op +5.79 ns/op / +7.1% (worse)
Windows MinGW AfterFuncZeroDelivery/Go 378.100 ns/op +7.3 ns/op / +2.0% (worse)
Windows MinGW AfterFuncZeroDelivery/LLGo 109780 ns/op -1751 ns/op / -1.6% (better)
Windows MinGW CreateStop/Go 89.070 ns/op -1.32 ns/op / -1.5% (better)
Windows MinGW CreateStop/LLGo 352.500 ns/op -0.5 ns/op / -0.1% (better)
Windows MinGW RearmStopped/Go 24.500 ns/op +0.01 ns/op / +0.04083% (worse)
Windows MinGW RearmStopped/LLGo 194.200 ns/op +4.8 ns/op / +2.5% (worse)
Windows MinGW ResetActive/Go 14.780 ns/op 0 ns/op / +0.0%
Windows MinGW ResetActive/LLGo 117.800 ns/op +1.5 ns/op / +1.3% (worse)
Windows MinGW ResetHeap1024/Go 14.780 ns/op -0.18 ns/op / -1.2% (better)
Windows MinGW ResetHeap1024/LLGo 99.910 ns/op +0.46 ns/op / +0.5% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/Go 779.300 ns/op -7.6 ns/op / -1.0% (better)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 128844 ns/op -1961 ns/op / -1.5% (better)
Windows MinGW 386 CreateStop/Go 169.300 ns/op -0.6 ns/op / -0.4% (better)
Windows MinGW 386 CreateStop/LLGo 391.100 ns/op +7.9 ns/op / +2.1% (worse)
Windows MinGW 386 RearmStopped/Go 54.520 ns/op -2.51 ns/op / -4.4% (better)
Windows MinGW 386 RearmStopped/LLGo 250.500 ns/op +2.8 ns/op / +1.1% (worse)
Windows MinGW 386 ResetActive/Go 32.600 ns/op +0.01 ns/op / +0.03068% (worse)
Windows MinGW 386 ResetActive/LLGo 844.500 ns/op +14.7 ns/op / +1.8% (worse)
Windows MinGW 386 ResetHeap1024/Go 32.880 ns/op +0.13 ns/op / +0.4% (worse)
Windows MinGW 386 ResetHeap1024/LLGo 140.800 ns/op +0.2 ns/op / +0.1% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 667.300 ns/op -1 ns/op / -0.1% (better)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 131725 ns/op -8239 ns/op / -5.9% (better)
Windows MinGW ARM64 CreateStop/Go 196.400 ns/op -1.9 ns/op / -1.0% (better)
Windows MinGW ARM64 CreateStop/LLGo 326 ns/op -20.4 ns/op / -5.9% (better)
Windows MinGW ARM64 RearmStopped/Go 70.690 ns/op +0.12 ns/op / +0.2% (worse)
Windows MinGW ARM64 RearmStopped/LLGo 206.900 ns/op -3.8 ns/op / -1.8% (better)
Windows MinGW ARM64 ResetActive/Go 31.070 ns/op +0.07 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetActive/LLGo 113.300 ns/op +14.38 ns/op / +14.5% (worse)
Windows MinGW ARM64 ResetHeap1024/Go 31.070 ns/op +0.04 ns/op / +0.1% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 110.700 ns/op +0.7 ns/op / +0.6% (worse)
Windows MSVC AfterFuncZeroDelivery/Go 641.800 ns/op -2.9 ns/op / -0.4% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 154490 ns/op -1837 ns/op / -1.2% (better)
Windows MSVC CreateStop/Go 182.300 ns/op +3.1 ns/op / +1.7% (worse)
Windows MSVC CreateStop/LLGo 681.200 ns/op -56.1 ns/op / -7.6% (better)
Windows MSVC RearmStopped/Go 67.420 ns/op -0.02 ns/op / -0.02966% (better)
Windows MSVC RearmStopped/LLGo 265 ns/op -27.7 ns/op / -9.5% (better)
Windows MSVC ResetActive/Go 30.170 ns/op -0.01 ns/op / -0.03313% (better)
Windows MSVC ResetActive/LLGo 205.700 ns/op +57.3 ns/op / +38.6% (worse)
Windows MSVC ResetHeap1024/Go 30.330 ns/op -0.36 ns/op / -1.2% (better)
Windows MSVC ResetHeap1024/LLGo 104 ns/op -1.7 ns/op / -1.6% (better)
Windows MSVC 386 AfterFuncZeroDelivery/Go 765.500 ns/op -3.1 ns/op / -0.4% (better)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 132304 ns/op +897 ns/op / +0.7% (worse)
Windows MSVC 386 CreateStop/Go 169.800 ns/op +3.3 ns/op / +2.0% (worse)
Windows MSVC 386 CreateStop/LLGo 364.700 ns/op -5.3 ns/op / -1.4% (better)
Windows MSVC 386 RearmStopped/Go 56.950 ns/op +0.27 ns/op / +0.5% (worse)
Windows MSVC 386 RearmStopped/LLGo 239.100 ns/op -12.1 ns/op / -4.8% (better)
Windows MSVC 386 ResetActive/Go 32.560 ns/op +0.04 ns/op / +0.1% (worse)
Windows MSVC 386 ResetActive/LLGo 883.500 ns/op -9.2 ns/op / -1.0% (better)
Windows MSVC 386 ResetHeap1024/Go 32.860 ns/op 0 ns/op / +0.0%
Windows MSVC 386 ResetHeap1024/LLGo 133.100 ns/op -0.6 ns/op / -0.4% (better)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 654.800 ns/op -9.6 ns/op / -1.4% (better)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 146668 ns/op +3179 ns/op / +2.2% (worse)
Windows MSVC ARM64 CreateStop/Go 200.500 ns/op -0.1 ns/op / -0.04985% (better)
Windows MSVC ARM64 CreateStop/LLGo 339.800 ns/op +3.9 ns/op / +1.2% (worse)
Windows MSVC ARM64 RearmStopped/Go 70.610 ns/op +0.11 ns/op / +0.2% (worse)
Windows MSVC ARM64 RearmStopped/LLGo 222.800 ns/op +0.4 ns/op / +0.2% (worse)
Windows MSVC ARM64 ResetActive/Go 30.890 ns/op -0.29 ns/op / -0.9% (better)
Windows MSVC ARM64 ResetActive/LLGo 106.900 ns/op -0.5 ns/op / -0.5% (better)
Windows MSVC ARM64 ResetHeap1024/Go 31.010 ns/op -0.1 ns/op / -0.3% (better)
Windows MSVC ARM64 ResetHeap1024/LLGo 117.800 ns/op +1.3 ns/op / +1.1% (worse)

Compared with 45bdf2db295a measured in the same runner job.

@cpunion
cpunion force-pushed the codex/wasm-reflect-worker-tables-20261006 branch 4 times, most recently from 6740337 to 2861a8d Compare October 6, 2026 08:42
@cpunion
cpunion force-pushed the codex/wasm-reflect-worker-tables-20261006 branch from 2861a8d to 2189079 Compare October 6, 2026 09:38
@cpunion
cpunion force-pushed the codex/wasm-reflect-worker-tables-20261006 branch 4 times, most recently from 16207fb to ad9784f Compare October 14, 2026 04:34

@visualfc visualfc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Non-blocking size follow-up: keep typed bridges as the default for JS reflection, but consider an explicit flag to restore libffi when cross-worker sharing is not needed.

Comment thread internal/build/wasm_reflect.go Resolved
@cpunion
cpunion force-pushed the codex/wasm-reflect-worker-tables-20261006 branch from 8ce2711 to 6fd8e06 Compare October 14, 2026 10:47
@cpunion
cpunion merged commit 7d83f15 into xgo-dev:main Oct 14, 2026
137 of 140 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.

2 participants