Skip to content

🐛 Avoid lambdas in comparison and scaling for LLVM 23 LTO - #6

Merged
kammce merged 1 commit into
libhal-2.6from
fix-compare-lambda
Oct 24, 2026
Merged

kammce merged 1 commit into
libhal-2.6from
fix-compare-lambda

Conversation

@kammce

@kammce kammce commented Oct 24, 2026

Copy link
Copy Markdown
Member

Replaces the lambdas in the framework's runtime comparison and scaling paths with named function templates, to work around an LLVM 23 LTO bug.

With clang 23.1.2, full LTO with -g produces a module that fails verification when a function template holding a lambda is instantiated both in a module unit and in a TU that imports it:

invalid retained nodes, retained node does not belong to subprogram

ld.lld aborts the link; Apple's ld64 warns and strips the debug info. LLVM 22.1.8 is not affected. A named function has no local type for the linker to merge, so it avoids the bug. Details and a minimal reproducer are in libhal/llvm-toolchain#59.

Changes:

  • compare_in_common_type(): the fallback comparison is now compare_in_common_type_fallback(). It is still called from both branches, so there is no fallthrough return for MSVC Debug C4702. The comment above it is a note for the mp-units maintainers.
  • div_round(): the remainder is now div_remainder().
  • scale_impl(): the floating-point scaling is now scale_fp_for().

libhal hit the first one on Linux CI (libhal/libhal#255): its spi module and that module's unit test both compared quantity<si::hertz, unsigned> values, and linking the test aborted.

Verified locally with LLVM 23.1.2 on macOS:

  • Built libhal against this branch in module mode (MinSizeRel), with libhal's own workaround reverted: the verifier error is gone and its 14 test suites pass.
  • conan build with user.mp-units.build:all=True in header mode: the static tests and header-set checks build, and 64/64 runtime tests pass.

Once merged, this needs a new @libhal release for libhal to pick it up.

With LLVM 23 (clang 23.1.2), full LTO with -g produces a module that
fails verification ("invalid retained nodes, retained node does not
belong to subprogram") when a function template holding a lambda is
instantiated both in a module unit and in a TU that imports it. ld.lld
aborts the link; Apple's ld64 warns and strips the debug info. LLVM
22.1.8 is not affected.

The lambdas in the framework's runtime paths become named function
templates, so there is no local type for the linker to merge:

- compare_in_common_type(): the fallback comparison is now
  compare_in_common_type_fallback(). The comment above it records the
  details for upstream.
- div_round(): the remainder is now div_remainder().
- scale_impl(): the floating-point scaling is now scale_fp_for().

libhal hit the first one on Linux x86_64 and arm64 (-std=c++23
-stdlib=libc++ -flto -g -Os -fsanitize=address, ld.lld): its spi module
and that module's unit test both compared quantity<si::hertz, unsigned>
values.

Verified by building libhal against this change with the hertz
comparison put back: the verifier error is gone and its tests pass. The
mp-units tests pass in header mode with LLVM 23 (build:all, 64/64).

@kammce kammce left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kammce
kammce merged commit b807562 into libhal-2.6 Oct 24, 2026
6 checks passed
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