Skip to content

[Build] Skip unchanged CompileNativeAssembly files and stabilize typemap outputs#3

Closed
davidnguyen-tech wants to merge 3 commits intomainfrom
feature/compile-native-assembly-skip-unchanged
Closed

[Build] Skip unchanged CompileNativeAssembly files and stabilize typemap outputs#3
davidnguyen-tech wants to merge 3 commits intomainfrom
feature/compile-native-assembly-skip-unchanged

Conversation

@davidnguyen-tech
Copy link
Copy Markdown
Owner

Summary

Two complementary optimizations that together eliminate ~2.85s of unnecessary LLVM llc recompilation on incremental CoreCLR builds.


Optimization 1: Per-file up-to-date check in CompileNativeAssembly

Adds per-file timestamp comparison in the CompileNativeAssembly task to skip recompilation of .ll files whose .o output is already up-to-date. Before running llc for each source file in RunAssembler(), the task checks if the output .o file exists and its last-write time is ≥ the input .ll file's last-write time. If so, the file is skipped with a debug log message:

[LLVM llc] Skipping 'environment.arm64-v8a.ll' because 'environment.arm64-v8a.o' is up to date

This preserves parallel compilation of changed files while skipping unchanged ones individually.

Optimization 2: Remove mvid_hash from TypeMapAssembly

Previously, typemap .ll files included a mvid_hash field computed from assembly MVIDs. Since MVIDs change on every rebuild (even when source is unchanged), this caused typemap .ll files to be byte-different on every incremental build — defeating Optimization 1 for typemap object files.

This commit removes the mvid_hash field and sorts typemap entries by assembly name instead. This makes typemaps.*.ll files byte-identical across incremental builds when no assembly content changes, allowing Optimization 1 to skip their recompilation.


Combined Impact

~2.85s saved per CoreCLR incremental build (measured on MAUI sample app). Without Optimization 2, only non-typemap .ll files benefit from the skip. With both optimizations, typemap .ll.o compilations are also skipped when assemblies are unchanged.

Build Status

Note: make currently fails due to a transient network issue fetching https://aka.ms/AndroidManifestFeed/d18-0. dotnet build Xamarin.Android.sln -c Debug compiles cleanly (0 errors).

Testing

  • Added CompileNativeAssemblySourcesSkipsUnchangedFiles integration test that verifies:
    • _CompileNativeAssemblySources target still runs (not skipped at target level)
    • At least one .ll file is skipped at the per-file level (via log message assertion)

davidnguyen-tech and others added 3 commits March 30, 2026 19:16
When _CompileNativeAssemblySources runs, it recompiles ALL .ll files
even if only some have changed. This is because MSBuild's target-level
Inputs/Outputs is all-or-nothing — if any .ll file is newer than any
.o file, every file gets recompiled.

Add a per-file timestamp check in RunAssembler() — if the output .o is
newer than the input .ll, that file is skipped. This saves time on
incremental CoreCLR builds where upstream generators use
CopyIfStreamChanged to preserve .ll timestamps for unchanged files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add CompileNativeAssemblySourcesSkipsUnchangedFiles integration test
that verifies on incremental CoreCLR builds, unchanged .ll files are
not recompiled by the CompileNativeAssembly task while changed .ll
files are still correctly compiled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove build-specific MVID data from the CoreCLR debug typemap assemblies
array, making the generated .ll files byte-identical across incremental
builds when only C# code changes (no type additions/removals).

Generator changes (TypeMappingDebugNativeAssemblyGeneratorCLR.cs):
- Remove mvid_hash field from TypeMapAssembly struct
- Remove MVID field (was already NativeAssembler-ignored)
- Sort data.UniqueAssemblies by name before building the assembly
  names blob, ensuring deterministic blob offsets
- Sort uniqueAssemblies list by Name instead of mvid_hash

Runtime changes (typemap.cc, xamarin-app.hh):
- Remove mvid_hash from the native TypeMapAssembly struct
- Replace MVID-based binary search with assembly iteration:
  for each assembly, construct "TypeName, AssemblyName" and look up
  in the managed-to-java map. The array is small (~80-100 entries),
  so iteration cost is negligible vs the previous hash+binary-search.

This eliminates the root cause of typemaps.*.ll content changes during
C#-only incremental edits. Combined with the per-file skip from commit
d2f07a1, this saves ~2.85s per CoreCLR incremental build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidnguyen-tech
Copy link
Copy Markdown
Owner Author

Superseded by dotnet#11055 (same branch in fork).

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.

1 participant