Skip to content

Reduce CoreCLR C++ exception cleanup overhead - #134835

Merged
davidwrighton merged 2 commits into
dotnet:mainfrom
davidwrighton:wasm-noexcept-code-size
Sep 29, 2026
Merged

davidwrighton merged 2 commits into
dotnet:mainfrom
davidwrighton:wasm-noexcept-code-size

Conversation

@davidwrighton

@davidwrighton davidwrighton commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Summary

Add accurate noexcept contracts to non-throwing CoreCLR cleanup and synchronization paths, and compile CoreCLR WASM code with -fassume-nothrow-exception-dtor. Together, these let Clang remove C++ exception regions that only exist to call std::terminate.

The main affected paths are metadata enumeration and release, exception cleanup, GC mode restoration, thread-store locking, frame unwinding, PAL resource cleanup, executable allocation cleanup, and common holder traits.

The compiler flag is limited to actual CoreCLR Browser/WASI native compilation. Cross-component builds do not use it.

Browser/WASM results

Built the optimized Browser subset with:

./build.sh clr -os browser -c Release --cmakeargs "-DCMAKE_C_FLAGS=-DNDEBUG -DCMAKE_CXX_FLAGS=-DNDEBUG -DCMAKE_C_FLAGS_RELEASE=-O3 -DCMAKE_CXX_FLAGS_RELEASE=-O3 -DCMAKE_EXE_LINKER_FLAGS_RELEASE=-O3"
Metric Baseline Changed Difference
dotnet.native.wasm 4,714,226 bytes 4,661,953 bytes -52,273 bytes (-1.11%)
Direct std::terminate sites 2,562 171 -2,391

-fassume-nothrow-exception-dtor lets Clang mark __cxa_end_catch as non-throwing. This removes the compiler-generated cleanup pads and terminate calls around exception-object destruction. The switch removes 1,053 terminate sites and 21,436 bytes beyond the source annotations alone. There are no explicit noexcept(false) destructors under src/coreclr or src/native.

Fourteen normalized functions gain 17 local terminate sites while removing 2,391 sites overall:

  • Frame::ExceptionUnwind gains two sites in centralized fail-fast/interpreter cleanup dispatch.
  • UnmapViewOfFile, CloseHandle, SafeRelease, PEImage::Release, and ExecutableAllocator::Release each gain one site in a shared cleanup leaf.
  • StringLiteralMap::GetInternedString gains one shared cleanup site.
  • Three compiler-generated global array destructors gain five sites, and LoaderHeap, GcInfoArrayListBase, GCHandleStore, and CLRException destructors each gain one. These are process-shutdown or cleanup paths.

These are cleanup-only or shared leaf handlers and are outweighed by the caller-side removals reflected in the aggregate count and binary size.

Linux x64 validation

  • ./build.sh clr+libs -rc Release -lc Release: passed with 0 warnings and 0 errors.
  • ./build.sh clr -c Checked: passed with 0 warnings and 0 errors.
  • cd src/tests && ./build.sh release: passed; 0 errors.
  • cd src/tests && ./run.sh Release: 4,330 passed, 0 failed, 250 skipped.
  • libcoreclr.so: 5,174,600 bytes baseline; 5,126,520 bytes changed; -48,080 bytes (-0.93%).

A local BenchmarkDotNet baseline-vs-changed comparison found no significant regression and identical allocations:

Benchmark Baseline Changed
Enumerate methods 563.1 ns 555.8 ns
Enumerate members 1,119.8 ns 1,113.1 ns
Enumerate assembly types 66.10 us 65.25 us

Note

This pull request description was generated with GitHub Copilot.

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

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 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: @agocke
See info in area-owners.md if you want to be subscribed.

Comment thread src/coreclr/inc/volatile.h
@davidwrighton

Copy link
Copy Markdown
Member Author

Need to try -fassume-nothrow-exception-dtor

@huoyaoyuan

Copy link
Copy Markdown
Member

Can/should we use noexcept for all (non-virtual) methods with NOTHROW contract?

@davidwrighton

Copy link
Copy Markdown
Member Author

@huoyaoyuan Using noexcept can cause extra generation of EH handling infrastructure, due to the interaction with std::terminate, so using it unconditionally is probably a poor practice unless we plan to perform this exercise to look for std::terminate usage commonly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidwrighton
davidwrighton marked this pull request as ready for review September 29, 2026 18:19
@davidwrighton

Copy link
Copy Markdown
Member Author

/ba-g failure was a transient failure, which passed on rerun, but Build Analysis could not discover that it was fixed.

@davidwrighton
davidwrighton merged commit 754e940 into dotnet:main Sep 29, 2026
166 of 168 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants