Skip to content

[browser][coreCLR] R2R: Interpreter PGO instrumentation (block counts) #130517

Description

@pavelsavara

Interpreter PGO instrumentation (block counts) for CoreCLR-on-WASM

Part of #130524.

Status: draft / issue candidate · Area: CoreCLR interpreter, PGO · Related: #130521 (WASM RyuJIT PGO enablement)

Goal

Make the CoreCLR interpreter a first-class profile producer so that hotness data collected while an app runs interpreted can drive precompilation (R2R) selection. This is the profile source the R2R-on-WASM ("R1") work needs: run the app on the interpreter, observe what is hot, and emit a standard profile that the existing precompilation toolchain consumes unchanged.

Why

Today the WASM app starts and runs on the interpreter. To decide what to precompile (the startup hot path, JSON, and later lazily-streamed code), we need a real profile of the interpreted run. The runtime already has a mature, well-understood profile format and downstream tooling; the missing piece is a producer on the interpreter side.

Direction

  • Count basic-block execution keyed by IL offset. The interpreter increments a counter at block heads (method entry and loop/branch targets), tagging each count with its IL offset. IL offsets are engine-independent, so the profile is portable and matches what the precompiler re-derives from the same IL.
  • Emit block counts, not edge counts. Deliberately avoid reproducing the JIT's edge/spanning-tree instrumentation scheme (which is shaped by the JIT's own flow-graph and internal blocks). Block counts are simpler, engine-independent, and the precompiler already derives branch likelihoods from block weights — so we get branching hints without coupling to JIT internals.
  • Reuse the shared profile representation. Populate the same profile schema/records the JIT already produces, so the collected data is format-identical and needs no special-casing in the profile tools or the precompiler.
  • Cheap and gated. A single-increment counter per instrumented point, stored in interpreter method metadata, enabled by the standard profiling switch and (optionally) scoped to a method filter to bound overhead during bring-up.
  • Export via existing profile paths. Once counts live in the shared representation, the runtime's existing profile-export mechanisms carry them out to the precompiler with no new format.

Scope

In: interpreter block-count instrumentation keyed by IL offset; storing counts in the shared profile representation; enabling collection during an interpreted run; producing a profile the precompiler accepts.

Out: edge-count profiles; JIT-side instrumentation and the broader feature enablement (that is #130521); consuming profiles / loading precompiled code.

Success criteria

  • An interpreter-only run yields a profile that the precompilation toolchain accepts without modification.
  • That profile selects essentially the same hot methods a JIT-collected profile would, for the same workload.
  • Measurable overhead of instrumentation is small and only paid when profiling is enabled.

Dependencies & relationship to #130521

Open questions

  • Which profile record kinds to emit (method-entry plus loop-head block counts) for good precompiler results.
  • Whether the profile tools and precompiler accept interpreter-produced, IL-offset-keyed counts with zero changes.
  • Divergence risk where the interpreter's block boundaries differ from the precompiler's at non-branch-target points; validate that loop heads and branch targets line up and that unmatched blocks degrade gracefully.

Note

This issue was drafted with GitHub Copilot assistance.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions