Skip to content

Fix __FINITE_MATH_ONLY__ guard to test the value, not definedness - #338

Draft
sivadeilra wants to merge 1 commit into
microsoft:mainfrom
sivadeilra:fix-finite-math-only-guard
Draft

Fix __FINITE_MATH_ONLY__ guard to test the value, not definedness#338
sivadeilra wants to merge 1 commit into
microsoft:mainfrom
sivadeilra:fix-finite-math-only-guard

Conversation

@sivadeilra

Copy link
Copy Markdown

Problem

XMVectorIsNaN, XMVector2IsNaN, XMVector3IsNaN and XMVector4IsNaN each guard a scalar fallback with:

#if defined(__clang__) && defined(__FINITE_MATH_ONLY__)

Clang defines __FINITE_MATH_ONLY__ unconditionally0 by default, and 1 only under -ffast-math / -ffinite-math-only (clang-cl /fp:fast). The guard tests definedness rather than the value, so it is always true under clang and the fallback is compiled into every clang build, not just finite-math ones.

Measured with clang-cl:

__FINITE_MATH_ONLY__ guard today guard after this change
default (/fp:precise) 0 true false
/fp:fast 1 true true

Consequences

  1. Build breakage. The fallback calls unqualified isnan(). Any clang target whose C++ library does not declare isnan() fails to compile. This is how we hit it: ~690 errors across 35 build directories in the Windows source tree, all use of undeclared identifier 'isnan' at these sites.
  2. Worse codegen where it does compile. A single vector compare is replaced by a store, four scalar tests and a reassemble.

MSVC never defines the macro, so it always takes the intrinsic path — which is why this is clang-only.

Change

Tests the macro's value instead. 8 sites: 4 functions × the NEON and SSE paths of each. No other file in the repo references the macro.

-#if defined(__clang__) && defined(__FINITE_MATH_ONLY__)
+#if defined(__clang__) && __FINITE_MATH_ONLY__

Preserves the file's CRLF line endings and ASCII-only content per .editorconfig.

Open question for maintainers

While investigating I measured what actually happens under /fp:fast, i.e. the only configuration in which the fallback is now selected. Under clang, every scalar NaN test folds to constant false there — __builtin_isnan, x != x, and even explicit union bit-inspection of the exponent field all become xor eax,eax; ret, because nnan/ninf let the optimizer prove no NaN can occur.

If that holds generally, the fallback does not do anything even when correctly selected, and deleting it may be more honest than re-guarding it. I have kept this PR to the minimal, clearly-correct fix and am raising the broader question rather than acting on it, since it is a behavioural decision for the maintainers.

Related: MSVC folds _mm_cmpneq_ps(V, V) to an all-zero mask under /fp:fast as well, so XMVectorIsNaN already returns "no lane is NaN" in fast-math MSVC builds. That is independent of this change.

XMVectorIsNaN, XMVector2IsNaN, XMVector3IsNaN and XMVector4IsNaN each guard a
scalar fallback with:

    #if defined(__clang__) && defined(__FINITE_MATH_ONLY__)

but clang defines __FINITE_MATH_ONLY__ unconditionally -- 0 by default, and 1
only under -ffast-math / -ffinite-math-only (clang-cl /fp:fast).  The guard is
therefore always true under clang, so the fallback is compiled into every clang
build instead of only finite-math ones.

Two consequences:

- The fallback calls unqualified isnan(), so any clang target whose C++ library
  does not declare isnan() fails to compile.  This is how the bug surfaced.
- Where it does compile, a single vector compare is replaced by a store, four
  scalar tests and a reassemble.

Test the macro's value instead.  Verified with clang-cl that the guard is now
false at the default /fp:precise and still true under /fp:fast.

Affects both the NEON and SSE paths of all four functions (8 sites).
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@walbourn walbourn self-assigned this Aug 4, 2026
@walbourn walbourn added the complier Issue related to compiler codegen label Aug 4, 2026
@walbourn

walbourn commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@sivadeilra For MSVC, a pragma turns off /fp:fast:

#if !defined(_XM_NO_INTRINSICS_) && defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#pragma float_control(push)
#pragma float_control(precise, on)
#endif

There is a bug where some older versions of MSVC ignored this pragma for inline code, but that is fixed in updates.

Are you using clang-cl or clang?

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

Labels

complier Issue related to compiler codegen

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants