Skip to content

fix(maul): keep Clang's trapping math off in the family's float flags - #129

Merged
siracozmen01 merged 1 commit into
mainfrom
fix/clang-trapping-math
Nov 5, 2026
Merged

siracozmen01 merged 1 commit into
mainfrom
fix/clang-trapping-math

Conversation

@siracozmen01

@siracozmen01 siracozmen01 commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The family's cmake/CompilerFlags.cmake passes -fno-unsafe-math-optimizations, and its comment says trapping math stays at each compiler's default, off in Clang. In Clang 19 and later that flag also turns trapping math on, which sets -ffp-exception-behavior=strict on every Clang build. Clang refuses strict floating-point exceptions on 32-bit ARM, so with the family's warnings as errors every file of a Maul library stops on Android armeabi-v7a with overriding currently unsupported use of floating point exceptions on this target [-Werror,-Wunsupported-floating-point-opt] (found by maul-rhi while building its drivers for 32-bit targets). A library with src/fpenv.c is refused on 32-bit ARM anyway, by design, since its float environment is known only on x86, AArch64 and WebAssembly; a library without it, such as maul-rhi, is stopped by this flag alone. On every other target Clang compiles in strict exception mode, which no Maul library reads: none calls fetestexcept, feclearexcept or uses FENV_ACCESS.

Change

The unsafe optimizations are turned off one by one, -fno-associative-math -fno-reciprocal-math -fsigned-zeros, in place of -fno-unsafe-math-optimizations, and the comment says why. Contraction stays off, errno math stays off, and a user's -funsafe-math-optimizations earlier on the command line is still undone.

Verification

  • NDK 28 Clang 19 with -std=c23 -Werror: the old flags fail on armv7a with the error above; the new flags compile on armv7a, aarch64, i686 and x86_64.
  • Clang's driver output (-###): the new flags give exactly Clang's defaults (no reassociation, reciprocal, signed-zero, approximate-function or exception option); after a user's -funsafe-math-optimizations they still remove every one of its options; the old flags added -ffp-exception-behavior=strict in both cases.
  • GCC 14: -Q --help=optimizers is identical between the old and the new flags with no user flag, after -funsafe-math-optimizations and after -ffast-math.
  • Host Clang 20 compiles with the new flags under -Werror.
  • maul-rhi (main 579fd6f) with this file: armeabi-v7a builds under NDK r28 with warnings as errors (main's flags stop every file), Android x86 builds, and on linux-clang-20 Release all 74 objects are byte-identical to the build with main's flags; its suite passes 86/86.
  • maul-audio (main 3a0a1d1) with this file, linux-clang-20 built from scratch with each flag set: every golden render and the spatializer bake rewrite byte-identical to the committed files, and the golden and nine spatializer suites pass on both.
  • maul-audio, the one library that calls libm on compile-time constants (echo_suppressor.c, octave_eq.c): its nine voice and EQ suites print the same 53 lines under both flag sets, and both files' objects disassemble identically, with identical constant sections, so nothing Clang folds there moves.

@siracozmen01 siracozmen01 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review at f3d3aff: no change needed, with two points not verified below. I read the diff and the family's float environment (maul-anim src/fpenv.h, a family file); I did not run a compiler.

Strict to ignore with contraction off, no reassociation, no reciprocal, signed zeros kept and round to nearest. Every + - * / and sqrt has one correctly rounded result in both modes, so whether Clang evaluates one at run time, folds it, hoists it, speculates it or drops a dead one, the bits are the same. What ignore mode adds over strict is freedom to move and fold, not freedom to change a finite result. The folds it allows and strict forbids are x * 1.0, x + -0.0 and x - 0.0 to x, plus commuting operands. Those differ from an IEEE evaluation only in NaN bits: a signaling NaN is not quieted, and with two NaN operands the other one's payload may come out. Arithmetic never makes a signaling NaN; only bytes read in as a float do. So a golden sees the difference only if it hashes a NaN taken from input bytes, and the libraries refuse NaN inputs.

The family already assumes ignore mode. fpenv.h says compilers do not order float arithmetic against writes to the control register, which is why every public call does its arithmetic in a NOINLINE worker. GCC (with trapping math on) and Clang before 19 never ordered them either. So no library relies on what strict mode gave, and the comment in this pull request now says the same.

Not verified. Please grep:

  1. libm calls with constant arguments. In ignore mode Clang may fold a libm transcendental (sinf, cosf, expf, logf, powf, atan2f and so on) whose argument is known at compile time, using its own evaluation in place of the target's libm. Strict mode did not fold those. A library that calls one on a constant, in a path a golden covers, could see its bits move on Clang. A grep of the libraries' src/ for those names settles it. If they use only the family's own math, nothing moves.
  2. x87 builds. If any family build does float math on x87 (32-bit x86 without SSE2), strict mode left inexact constant expressions to run time, in x87's wider precision, while ignore mode folds them correctly rounded. Android's i686 ABI has SSSE3, so LLVM uses SSE there and this does not apply. I only ask in case another 32-bit x86 build exists.

For maul-rhi's armv7 run (from memory, not checked here): NEON on ARMv7 always flushes denormals, and LLVM keeps float math off NEON on ARMv7 unless unsafe-math flags allow it, which this flag set does not. The goldens-unchanged run you plan will confirm it. Separately, fpenv.h names MXCSR, FPCR and WebAssembly only, so that build will also show whether the environment code handles 32-bit ARM's FPSCR.

The body has Problem, Change and Verification, and has no column-0 word( or type(scope): line.

@siracozmen01
siracozmen01 merged commit 374693e into main Nov 5, 2026
1 check failed
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