Skip to content

math: guard __builtin_roundeven against non-glibc platforms#1398

Merged
mr-c merged 2 commits intosimd-everywhere:masterfrom
kjg0724:fix/roundeven-non-glibc
Mar 27, 2026
Merged

math: guard __builtin_roundeven against non-glibc platforms#1398
mr-c merged 2 commits intosimd-everywhere:masterfrom
kjg0724:fix/roundeven-non-glibc

Conversation

@kjg0724
Copy link
Copy Markdown

@kjg0724 kjg0724 commented Mar 27, 2026

Problem

roundeven() and roundevenf() are C23 functions added to glibc 2.25 (2017).
On non-glibc platforms (OpenBSD, musl, Alpine Linux, etc.) they are absent.

The original condition in simde-math.h:

#if \
   (... && HEDLEY_HAS_BUILTIN(__builtin_roundeven)) || \
    HEDLEY_GCC_VERSION_CHECK(10,0,0)
  #define simde_math_roundeven(v) __builtin_roundeven(v)

On GCC 10+, __has_builtin (introduced in GCC 10) returns true for
__builtin_roundeven (also introduced in GCC 10), so the
HEDLEY_HAS_BUILTIN branch is taken. GCC lowers this builtin to a
roundeven() libm call when the hardware has no native instruction.
On platforms without roundeven in libm, this causes a link error:

undefined reference to `roundevenf'

This was observed building VVenC (which bundles SIMDE) on OpenBSD with
GCC 13: fraunhoferhhi/vvenc#680

Note: HEDLEY_GCC_VERSION_CHECK(10,0,0) in the original || branch
was dead code — on GCC 10+ the HEDLEY_HAS_BUILTIN branch always
fires first.

Fix

Guard the HEDLEY_HAS_BUILTIN path so that GCC 10+ only uses the
builtin when glibc ≥ 2.25 is present. Non-glibc platforms and old
glibc fall through to the portable inline fallback.

#if \
   ((!defined(HEDLEY_EMSCRIPTEN_VERSION) || HEDLEY_EMSCRIPTEN_VERSION_CHECK(3, 1, 43)) && \
     HEDLEY_HAS_BUILTIN(__builtin_roundeven) && \
     (!HEDLEY_GCC_VERSION_CHECK(10,0,0) || \
      (defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))))

Why Clang is unaffected: Clang lowers __builtin_roundeven to
llvm.roundeven.* intrinsics, which LLVM always emits inline (no libm
dependency). The !HEDLEY_GCC_VERSION_CHECK(10,0,0) branch stays open
for Clang on all platforms.

Why glibc 2.25: roundeven() was added in glibc 2.25 (2017).
Checking only defined(__GLIBC__) would allow glibc 2.24 and earlier,
where the symbol is also absent.

Both simde_math_roundeven (double, line 1271) and
simde_math_roundevenf (float, line 1293) are fixed identically.

Copy link
Copy Markdown
Collaborator

@mr-c mr-c left a comment

Choose a reason for hiding this comment

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

Thank you!

@mr-c
Copy link
Copy Markdown
Collaborator

mr-c commented Mar 27, 2026

@kjg0724 if you could add one architecture + compiler combination to our CI, which would have caught this issue sooner, what would it be?

@kjg0724
Copy link
Copy Markdown
Author

kjg0724 commented Mar 27, 2026

GCC 10+ on Alpine Linux (musl libc) with SIMDE_NO_NATIVE would have caught this. Alpine's musl lacks roundeven(), and SIMDE_NO_NATIVE ensures the scalar path is taken, forcing GCC to emit an actual roundeven() libm call rather than inlining it as a ROUNDSD/FRINTN instruction. This would be straightforward to add as a Docker-based CI job, complementing the existing glibc-based Ubuntu/Fedora coverage.

@mr-c
Copy link
Copy Markdown
Collaborator

mr-c commented Mar 27, 2026

GCC 10+ on Alpine Linux (musl libc) with SIMDE_NO_NATIVE would have caught this. Alpine's musl lacks roundeven(), and SIMDE_NO_NATIVE ensures the scalar path is taken, forcing GCC to emit an actual roundeven() libm call rather than inlining it as a ROUNDSD/FRINTN instruction. This would be straightforward to add as a Docker-based CI job, complementing the existing glibc-based Ubuntu/Fedora coverage.

Thank you! Can you add this to our CI matrix? You are welcome to use whichever CI provider you please (minor bonus if it isn't GitHub Actions as we have so many there already).

@mr-c
Copy link
Copy Markdown
Collaborator

mr-c commented Mar 27, 2026

And if you could squash your existing 3 commits into one, that would be appreciated. I can do it myself, but maybe you would craft the combined commit message better than I would.

@mr-c mr-c linked an issue Mar 27, 2026 that may be closed by this pull request
roundeven() / roundevenf() are C23 functions added to glibc 2.25 (2017).
On GCC 10+, HEDLEY_HAS_BUILTIN(__builtin_roundeven) returns true (both
__has_builtin and __builtin_roundeven were introduced in GCC 10), so the
builtin path is taken. GCC lowers this to a roundeven() libm call when
the target has no native instruction (ROUNDSD/FRINTN). On non-glibc
platforms (musl, OpenBSD, MinGW, etc.) this symbol is absent, causing a
link error:

  undefined reference to 'roundevenf'

Note: the previous HEDLEY_GCC_VERSION_CHECK(10,0,0) branch in the ||
condition was dead code — on GCC 10+ HEDLEY_HAS_BUILTIN already fires.

Fix: guard the HEDLEY_HAS_BUILTIN path so GCC 10+ only uses the builtin
when glibc >= 2.25 is confirmed. Non-glibc platforms and older glibc fall
through to the portable inline fallback (roundf + fabsf), which requires
no libm symbols beyond what every C platform provides.

Clang is unaffected: it lowers __builtin_roundeven to llvm.roundeven.*
intrinsics which are always emitted inline (no libm dependency), so the
GCC guard does not apply there.

Both simde_math_roundeven (double) and simde_math_roundevenf (float) are
fixed identically.

Fixes link failure observed on OpenBSD/GCC building VVenC with bundled
SIMDE: fraunhoferhhi/vvenc#680
@kjg0724 kjg0724 force-pushed the fix/roundeven-non-glibc branch from 2baf39d to fd3d458 Compare March 27, 2026 10:30
kjg0724 pushed a commit to kjg0724/vvenc that referenced this pull request Mar 27, 2026
GCC 10+ lowers __builtin_roundeven/__builtin_roundevenf to libm calls
(roundeven/roundevenf), which are C23 additions only available in glibc
>= 2.25. On non-glibc platforms (musl, OpenBSD, Alpine Linux, MinGW)
these symbols don't exist, causing linker errors.

Fix: restrict the HEDLEY_HAS_BUILTIN path so GCC 10+ only uses the
builtin when glibc >= 2.25 is confirmed. Non-glibc systems and older
glibc versions fall through to the portable inline fallback.

Clang is unaffected because it lowers to LLVM intrinsics emitted inline.

Mirrors fix from simd-everywhere/simde#1398.

https://claude.ai/code/session_01QVvBbKu7xsm96hQUgvqd9E
kjg0724 pushed a commit to kjg0724/simde that referenced this pull request Mar 27, 2026
Add a Cirrus CI task that builds and tests SIMDE on Alpine Linux (musl
libc) with GCC and SIMDE_NO_NATIVE disabled.

This combination catches link errors caused by __builtin_roundeven being
lowered to a roundeven() libm call on GCC 10+: musl lacks roundeven()
(C23), so the linker error surfaces immediately. SIMDE_NO_NATIVE ensures
the scalar path is taken even on x86-64 with SSE4.1, preventing GCC from
inlining the builtin as a ROUNDSD instruction and masking the issue.

Suggested during review of simd-everywhere#1398.
Add a Cirrus CI task that builds and tests SIMDE on Alpine Linux (musl
libc) with GCC and SIMDE_NO_NATIVE disabled.

This combination catches link errors caused by __builtin_roundeven being
lowered to a roundeven() libm call on GCC 10+: musl lacks roundeven()
(C23), so the linker error surfaces immediately. SIMDE_NO_NATIVE ensures
the scalar path is taken even on x86-64 with SSE4.1, preventing GCC from
inlining the builtin as a ROUNDSD instruction and masking the issue.

Suggested during review of simd-everywhere#1398.
@mr-c mr-c enabled auto-merge (rebase) March 27, 2026 12:45
@mr-c mr-c merged commit df69d38 into simd-everywhere:master Mar 27, 2026
131 of 132 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.

ld: error: undefined reference: roundevenf

3 participants