Skip to content

Verify #8078 removes the split-module unknown-wrapper collision #8064

Closed

Description

@proggeramlug

Status

The code fix is merged on main:

  • PR: Scope split unknown-function fallback wrappers by module #8078
  • audited head: 6772b8111aeeb047d4ebbcebcfce7a0e8a7e9736
  • merge commit: 5c27d1ad9b588f69fa2677acd8ae99dc5303e712
  • focused emitted-IR regression: passed
  • two separately split modules + real shared object link regression: passed
  • landing-tree perry-codegen suite: 996/996 passed

The original global symbol ___perry_wrap_perry_unknown_func is replaced by stable module-scoped fallback names while unsplit copies retain internal linkage.

This ticket now has one bounded purpose: verify that the original production-sized duplicate-symbol boundary is gone. It is not a tracker for every later Next.js, provider, GC, or App Route problem.

Original production boundary

On b0f13fb5ef39eb26463d4bc82f7cd6812acea69f, the pinned Next.js 16.3.0 webpack App Route graph from #8034:

  • passed the byte-identical 21-request Node oracle;
  • collected and emitted 91/91 Perry module objects;
  • cleared the former split-unit ptrtoint failures;
  • failed the final app-only dylib link because four objects exported the same ___perry_wrap_perry_unknown_func.

The old local replay log was:

/Users/amlug/cargo-targets/next-prod-gate/evidence/perry-route-compile-b0f13fb5.log

Do not depend on that path existing. Regenerate the pinned fixture from #8034 when necessary.

Remaining worker task

  1. Fetch current main and record the exact commit/tree.

  2. Generate/build the pinned Next.js 16.3.0 webpack fixture from [Next.js/dylib] Add a pinned production App Route parity fixture and CI gate #8034. Confirm the Node oracle inputs are the generated production route graph; a direct call to the user GET export is not a substitute.

  3. Build one coherent release artifact set from that same Perry SHA in an isolated target:

    cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static -p perry-ext-http -p perry-ext-net

  4. Record SHA-256, size, and mtime for perry, libperry_runtime.a, libperry_stdlib.a, libperry_ext_http.a, and libperry_ext_net.a; prove no writer remains before the replay.

  5. Compile the same app-only graph from scratch with the isolated release directory pinned:

    PERRY_RUNTIME_DIR=<target>/release PERRY_NO_AUTO_OPTIMIZE=1 PERRY_NO_CACHE=1 <target>/release/perry compile --no-cache --no-auto-optimize --output-type dylib perry-host.js -o next-app-route-current.dylib

  6. Preserve the full module-emission/final-link log and report the exact final boundary.

Closure rule

Close #8064 after exactly one of these outcomes:

Do not keep #8064 open for:

  • executing the 21 Perry requests;
  • AppRouteRouteModule.handle behavior;
  • provider ABI/runtime behavior after a successful link;
  • moving-GC or verifier evidence;
  • performance.

Those belong to #8034/#8040 or a new dedicated blocker. This issue is complete once the collision itself is disproved on the pinned production graph.

Refs #8034 and #8040.

Activity

  1. changed the title [-]Split modules collide on global unknown-function fallback wrapper[/-] [+]Verify #8078 removes the split-module unknown-wrapper collision[/+] on Aug 14, 2026
  2. added
    bugConfirmed defect or regression
    help wantedExtra attention is needed
    triagedMaintainer reviewed; type, scope, and next step are clear
    on Aug 14, 2026
  3. proggeramlug commented on Aug 14, 2026

    @proggeramlug
    ContributorAuthor

    Updated after #8078 merged. This ticket is now intentionally bounded to one current-main replay proving that the original duplicate unknown-wrapper symbol is absent. A later, causally distinct failure must move to a new issue and must not keep #8064 open. The issue body now contains the exact artifact build, replay steps, evidence requirements, and closure rule.

  4. proggeramlug commented on Aug 14, 2026

    @proggeramlug
    ContributorAuthor

    Verified: the duplicate unknown-wrapper collision is gone on the pinned production graph

    Replay completed against the bounded closure rule. Outcome is Success (first bullet): the 91-module graph reached the final link and the app-only dylib was produced with no duplicate unknown-wrapper symbol.

    Artifact set

    Built from 5c27d1ad9b588f69fa2677acd8ae99dc5303e712 (the #8078 merge commit) in an isolated target. Hashes re-verified after the replay and identical to the pre-replay snapshot (lsof_writers=0 at snapshot time):

    424670fc6d21515d10881fc984abb73691fd8553349fc5dfc1f7cefe6b240b39  perry
    703ed8f50cb4f1ea606b436e9d1d46fb80698a57bb5d6d8b96f12f437211925a  libperry_runtime.a
    0da75064e91fb2ccb5c575bcc6798e49db78f6e7ebe3028a6fff8c228dbf2b0d  libperry_stdlib.a
    f2c971840f2d4e677f6b360b4f23205ff9a50a14c3e927496500f03cb769be0e  libperry_ext_http.a
    b83f3f9d354124269bca9730252fe9988b1d180197a91b4c7919c02edf055636  libperry_ext_net.a
    

    Oracle

    The pinned Next.js 16.3.0 webpack App Route graph from #8034, exercised through the generated production route (not a direct call to the user GET export):

    PASS: 21 production App Route requests
    

    Replay result

    • 91/91 modules emitted (Wrote object file: x91), no Error compiling module, no PERRY_ALLOW_PARTIAL_CODEGEN.
    • Final link succeeded: Linking (with stdlib)... Wrote shared library: next-app-route-current.dylib (239 MB).
    • PERRY_RUNTIME_DIR pinned to the isolated release dir, confirmed in-log by the libperry_ext_http.a / libperry_ext_net.a routing lines resolving under that target.

    The discriminating evidence

    A clean link on its own would be vacuous — it is also what you get if no wrapper is emitted at all. So the wrapper population in the produced dylib was counted directly:

    nm -a next-app-route-current.dylib | grep '___perry_wrap_perry_unknown_func$'   -> 0
    nm -a next-app-route-current.dylib | grep ' T ___perry_wrap_perry_unknown_func' -> 4
    nm -a next-app-route-current.dylib | grep ' t ___perry_wrap_perry_unknown_func' -> 9
    
    • The original global name ___perry_wrap_perry_unknown_func is absent (0 occurrences of the unsuffixed symbol).
    • 13 wrappers were emitted, so the subject was live.
    • Exactly 4 carry external linkage, each with a distinct module-scoped suffix — matching the original failure precisely, which was four objects exporting the same symbol:
    T ___perry_wrap_perry_unknown_func_fixture_node_modules_next_dist_compiled_jsonwebtoken_index_js
    T ___perry_wrap_perry_unknown_func_fixture_node_modules_next_dist_compiled_next_server_app_page_runtime_prod_js
    T ___perry_wrap_perry_unknown_func_fixture_node_modules_next_dist_compiled_next_server_app_route_runtime_prod_js
    T ___perry_wrap_perry_unknown_func_route_stage_o0_validation__next_server_chunks_2_js
    
    • The remaining 9 unsplit copies retain internal linkage, as the fix describes.

    One deviation, stated plainly

    Three earlier attempts on this host died from resource exhaustion, not from the product: two SIGBUS and two ENOSPC (the machine was down to a few GiB). The accepted run therefore lowered PERRY_LL_O0_THRESHOLD_BYTES/PERRY_LL_SIZE_OPT so oversized modules compile at -O0 rather than the default -Os downgrade (#4880).

    That knob only selects LLVM's optimization pipeline for oversized modules. The unknown-wrapper names are chosen in Perry's own codegen, upstream of LLVM, and external symbols are not DCE'd at any level — so it cannot manufacture or hide the collision. The wrapper census above confirms this directly: the wrappers were emitted, and the four link-visible ones are the exact set that previously collided.

    Closing as fixed by #8078. Later App Route behavior, request execution, provider ABI, GC, and performance stay with #8034 / #8040 per the closure rule.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionhelp wantedExtra attention is neededtriagedMaintainer reviewed; type, scope, and next step are clear

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions