[browser][coreCLR] R2R: IL splitter — shard trimmed assemblies by namespace
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
# IL splitter: shard trimmed assemblies by namespace (build-time producer)
Part of #130524.
Status: **draft / issue candidate** · Area: WASM build tasks, trimming, packaging · Related: #130515 (shard consumer), #130522 (R2R over in-memory IL), D2
## Goal
A build-time task that splits large, trimmed, IL-only application and library assemblies into smaller **namespace-aligned shard assemblies**, so later phases (precompilation, packaging, and eventually lazy download) can work at sub-assembly granularity. The unit of work and of download drops from a whole assembly to a namespace-sized shard. Secondary motivation: the same shard layout later enables lazy IL download (including under JSPI), but that consumer is separate (#130515).
## Position in the pipeline
Runs **after IL trimming** (it operates on already-trimmed assemblies) and **before precompilation and packaging**. It emits the shards, a facade for the original assembly, and per-shard metadata, and it updates the assembly lists that downstream phases consume so they pick up the new layout automatically.
## Direction
- **Shard by namespace; namespace is atomic.** Pack multiple small namespaces into one shard to approach a configurable **optimal size**; a namespace larger than optimal becomes its own oversized shard. Never split a single namespace across shards.
- **Merge cyclic namespaces.** Because assembly references cannot form cycles, group mutually-dependent namespaces together (by strongly-connected components) so no two shards reference each other cyclically.
- **Facade + type-forwarders.** The original assembly identity is preserved as a **facade** that keeps the unmovable content — types with no namespace (global namespace), module-level and compiler-internal types, forced-keep types, and assembly-level metadata — and forwards every moved type to the shard that now holds it. External references to the original assembly keep resolving; resolution follows the forwarder to the shard. The forwarder therefore already names the shard to bring in, so the **shard assembly name is the primary locate key** and namespace is a secondary index.
- **Per-shard namespace metadata.** Each shard records the namespace(s) it contains; this becomes part of the shard's asset entry in the boot configuration, so tooling and the runtime can associate a namespace with its shard (useful for grouping and pre-fetch).
- **Boot-config assets: IL shards vs. R2R-only shards.** IL shards are ordinary assembly assets from the boot configuration's perspective. In addition, the task extends the boot-config (boot json) generation to emit a new **R2R-only asset type** for lazily-loaded R2R shards — distinct from ordinary assembly assets and from eagerly-loaded startup R2R — carrying the sizing/locating metadata the runtime needs to fetch and instantiate a shard on demand. The live hotness trigger (#130516) consumes this R2R-only asset.
- **Update downstream lists.** The task rewrites the relevant assembly item lists so subsequent phases consume the facade plus shards instead of the original assembly.
- **Incremental.** Keyed on input assemblies; unchanged inputs are not re-split.
- **Excludes the core library.** The base class library assembly is out of scope (too central and entangled, and precompiled/handled specially).
## Scope
**In:** the splitting task; facade and type-forwarder emission; cyclic-namespace merging; size packing to the optimal-size parameter; per-shard namespace metadata; boot-config generation extended with the new R2R-only asset type (IL shards remain ordinary assembly assets); downstream assembly-list updates; incrementality; exclusion of the core library.
**Out:** the runtime consumer that loads shards on demand (#130515); JSPI; precompilation itself.
## Success criteria
- A trimmed app builds into a facade plus namespace shards near the target size; downstream phases consume them transparently; the app runs identically.
- Each shard carries its namespace metadata into the boot configuration.
- Rebuilds are incremental with respect to input assemblies.
## Open questions
- Reflection/identity fidelity: how much reflection over the original assembly must keep working (type-forwarders handle type resolution; some assembly-enumeration or exotic reflection may observe the split). Best-effort for apps; validate.
- Naming and versioning of shard assemblies (uniqueness, signing) — the prototype may keep this simple.
- Coupling that crosses namespaces (nested/partial types, generics referencing other namespaces) — ensure the strongly-connected-component grouping captures all of it.
- Interaction with precompilation (D2 assembly-level shards) and with the R2R metadata reuse (#130522) across the facade+shard layout.
> [!NOTE]
> This issue was drafted with GitHub Copilot assistance.
Contributor guide
Assessment
This issue has not been assessed yet.