Skip to content

[Experimental] Add MSBuild task-level cache for crossgen2 invocations - #135446

Draft
sbomer wants to merge 43 commits into
mainfrom
sbomer/crossgen-cache
Draft

sbomer wants to merge 43 commits into
mainfrom
sbomer/crossgen-cache

Conversation

@sbomer

@sbomer sbomer commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Stacked on #135886, which extracts the shared cache lifecycle and storage. Adds an experimental MSBuild task-level crossgen2 cache, enabled only by CROSSGEN2_EXPERIMENTAL_CACHE=true, with optional CROSSGEN2_EXPERIMENTAL_CACHE_PATH. The initial scope is directly executed Linux-x64 and Windows-x64 compilers targeting the corresponding host, with non-composite PE output and supported profiles/symbols.

Crossgen2 uses #135886's shared output contracts and SHA-256 hashing/restore/store pipeline; the task retains eligibility, input discovery, output preparation, and diagnostic policy. It declares owned output files, including absent symbols, without clearing unrelated neighbors. This PR adds optional task-output metadata callbacks so cache hits restore WarningsDetected. Neither cache replays compiler diagnostic text.

Earlier Linux measurements showed CoreLib compilation falling from about 7.9 seconds to a 90 ms hit with byte-identical output, and a 100-assembly workload falling from about 24.4 to 6.7 seconds. Latest analyzer-enabled validation passed 202 crossgen tests, including three real-compiler cases, and 65 maintenance-tool tests; six Windows-only cases were skipped. Net472 was previously built but not executed on Linux.

This remains a draft experiment without automatic enablement or eviction, size limits, or a stable-format guarantee. External managed-runtime/hosting dependencies must remain stable; unsupported invocations visibly bypass caching. The mirrored task changes and common sources still need a coordinated SDK port. See src/tasks/Crossgen2Tasks/README.md for the precise identity inventory and supported contract.

Note

This content was created with assistance from AI.

sbomer and others added 2 commits October 5, 2026 13:12
Adds an opt-in, environment-gated cache for RunReadyToRunCompiler,
covering native Linux-x64, non-composite PE crossgen2 compilation.
Crossgen2Cache computes a key from full SHA-256 hashes of the task
assembly, compiler, JIT, inputs/references/profiles, and effective
arguments/options, then restores or stores the declared outputs
(image + perf map) with atomic publish via cache-local staging and
rename. Cache hits replay captured diagnostics and preserve the
original exit code. Unsupported invocations (composites, legacy
crossgen, extra arguments, non-Linux-x64, managed-host deployment)
visibly bypass caching.

Adds Crossgen2Tasks.Tests with unit, negative-control, and real
crossgen2 integration coverage, and documents the contract,
hashed-input scope, deliberate omissions, and dotnet/sdk coordination
needed in README.md.

Assisted-by: GitHub Copilot CLI:claude-sonnet-5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Real crossgen2 invocations driven through Microsoft.NET.CrossGen.targets
(including the CoreLib build) always set Crossgen2ContainerFormat="pe"
(the file's own default when unset) and pass extra, pathless boolean
switches such as --embed-pgo-data. Both unconditionally bypassed the
cache, so it never engaged for any real-world caller despite working in
synthetic tests.

- Treat an explicit "pe" container format the same as unset: both mean
  plain PE output, the only format this cache supports.
- Replace the all-or-nothing extra-argument bypass with a small allowlist
  of known, pathless boolean switches actually used by in-repo callers.
  Each is still included verbatim in the cache key, so a different value
  still produces a different entry; anything unrecognized still bypasses.

Verified end-to-end against the real CoreLib crossgen2 invocation: a
cold run now populates the cache (cache miss/store), and a subsequent
run restores from it (cache hit), with byte-identical output and no
change to bypassed/unsupported shapes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/crossgen-contrib
See info in area-owners.md if you want to be subscribed.

Copilot AI 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.

🟡 Changes recommended

The test suite is not integrated as an automated test project, and the new environment variables violate repository naming conventions.

5 open findings
What changed in this PR

Adds an opt-in, content-addressed cache for supported Linux-x64 crossgen2 task invocations.

Changes:

  • Implements atomic cache storage, validation, restoration, and diagnostic replay.
  • Adds eligibility checks and documentation for supported invocation shapes.
  • Adds unit and optional real-compiler tests.
File Description
src/​tasks/​tasks.proj Excludes the test project from task traversal.
src/​tasks/​Crossgen2Tasks/​RunReadyToRunCompiler.cs Integrates caching into compiler execution.
src/​tasks/​Crossgen2Tasks/​README.md Documents configuration and cache behavior.
src/​tasks/​Crossgen2Tasks/​Crossgen2Tasks.csproj Exposes internals to tests.
src/​tasks/​Crossgen2Tasks/​Crossgen2Cache.cs Implements cache identity, storage, and restoration.
src/​tasks/​Crossgen2Tasks.Tests/​Crossgen2Tasks.Tests.csproj Defines the new test project.
src/​tasks/​Crossgen2Tasks.Tests/​Crossgen2CacheTests.cs Tests cache behavior and concurrency.

🧠 Review effort: Balanced

Comment thread src/tasks/tasks.proj Resolved
sbomer and others added 3 commits October 9, 2026 09:38
Register the non-shipping test project in clr.toolstests, make the unreadable-profile case independent of Unix permissions, and clarify the extra-argument bypass diagnostic.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Normalize the IL directory separately from the item glob so MSBuild supplies individual reference files. The function-wrapped wildcard was passed literally to crossgen2 and made the invocation cache bypass because it could not fingerprint those references. Uncached compilation and cached restoration produce byte-identical images and perf maps.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
sbomer and others added 2 commits October 13, 2026 11:36
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cc6353a6-7def-4dd2-8d2a-2914edbec8ad
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cc6353a6-7def-4dd2-8d2a-2914edbec8ad
Cache response-file path SearchValues and use Convert.ToHexString while preserving uppercase SHA-256 identities. Keep hashing the on-disk task assembly, explicitly bypass caching when its location is unavailable, and narrowly suppress the single-file analyzer warning at the guarded lookup.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
sbomer and others added 4 commits October 15, 2026 11:46
Assisted-by: GitHub Copilot CLI:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Keep ILLink configuration and input identity in the task while sharing entry validation, publication, directory restoration, and usage metadata. Preserve v1 serialization, mutex names, diagnostics, and purge semantics. Compile shared sources into consumers without adding a runtime assembly.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Model owned directories and declared file sets in shared source. Keep tool identity and diagnostics in the task adapters, and preserve ILLink's directory format.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
@dotnet-policy-service dotnet-policy-service Bot added the linkable-framework Issues associated with delivering a linker friendly framework label Oct 15, 2026
Validate rooted and adjacent CodeView PDB candidates before cleanup or cache restoration in every invocation mode. Share candidate discovery with cache identity and cover enabled, disabled, bypassed, composite and legacy invocations.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541

Copilot AI 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.

🟡 Changes recommended

Windows drive-relative CodeView paths fail unconditional task validation instead of bypassing caching, including when caching is disabled.

1 open issue
1 resolved since last review

Review effort: Balanced

Keep drive-relative CodeView candidates available for ordinary per-drive overlap validation while cache input validation continues to reject them. Extend Windows coverage for cache-disabled execution and root/adjacent PDB overlap protection.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541

Copilot AI 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.

🟡 Changes recommended

Wildcard inputs are validated as literal paths, allowing overlap checks to miss compiler inputs that ExecuteNormally can delete before compilation, even when caching is disabled.

1 open issue
1 resolved since last review

Review effort: Balanced

Expand crossgen2 assembly and reference patterns before overlap and associated-symbol validation while retaining wildcard cache bypass. Cover reference and composite inputs, stale symbols, disabled caching, and non-overlapping patterns.

Assisted-by: GitHub Copilot CLI:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541

Copilot AI 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.

🔵 Needs a closer look

Human assessment is needed because inconsistent input normalization can allow cleanup to delete an actual compiler input, even when caching is disabled.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Base automatically changed from sbomer/shared-invocation-cache to main October 29, 2026 17:34
sbomer added a commit that referenced this pull request Oct 29, 2026
Extracts ILLink's experimental invocation cache into shared sources,
compiled into each consumer without adding a shared DLL. Tasks provide
invocation descriptions and prepare their outputs before executing; the
shared engine handles content hashing, lookup, restoration, storage, and
purge. This intentionally goes beyond a mechanical extraction: the
common output contract supports owned directories and file sets, records
absent outputs, and uses checksummed manifests. That changes the
experimental v1 entry format: existing entries are not reused, but
remain purgable. #135446 builds on this infrastructure to add crossgen2
caching.

Replaces the ILLink-specific maintenance package and command with
`dotnet-build-cache`, shared by both caches. Existing users should
install that package instead of `dotnet-illink-cache` and replace
`dotnet illink-cache purge ...` with `dotnet build-cache purge --cache
illink ...`. Commands already selecting an explicit directory can use
`dotnet build-cache purge --cache-directory <path> ...` without a cache
name. The cache-enablement environment variables are unchanged.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
sbomer and others added 2 commits October 29, 2026 11:13
Assisted-by: Copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541
Protect leading-whitespace primary and composite assembly paths and their associated PDBs before output cleanup, regardless of cache eligibility.

Assisted-by: Copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541

Copilot AI 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.

🟡 Changes recommended

Validation mistakes the native-PDB output record for an input dependency, blocking Windows .NET 5 PDB-only compilation before /CreatePDB runs even when caching is disabled.

1 open issue

Review effort: Balanced

Preserve PDB-only compilation of native images without weakening protection of original IL symbols or reference inputs.

Assisted-by: GitHub Copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da7cf090-9a92-453d-beb2-f672a9a7b541

Copilot AI 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.

🔵 Needs a closer look

Human assessment is needed because inconsistent symbol-path whitespace handling can misdirect cleanup and overlap validation, risking deletion of unrelated files while leaving stale compiler outputs.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

This branch has not been deployed

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

Labels

area-ReadyToRun linkable-framework Issues associated with delivering a linker friendly framework

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants