[libc] [llvm] [libc][fenv] Refactor x86 fenv implementations to make it work for various fenv_t. (PR #165015)

via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 10 13:48:15 PST 2025


================
@@ -0,0 +1,261 @@
+//===-- x87 floating point env manipulation functions -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X86_COMMON_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_FENV_X86_COMMON_H
+
+#include <stdbool.h>
+
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/fenv_t.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/architectures.h"
+#include "src/__support/macros/properties/compiler.h"
+#include "src/__support/macros/properties/cpu_features.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+namespace internal {
+
+// Default order of floating point exception flags in x87 and mxcsr registers:
+// - Bit 0: Invalid Operations
+// - Bit 1: Denormal
+// - Bit 2: Divide-by-zero
+// - Bit 3: Overflow
+// - Bit 4: Underflow
+// - Bit 5: Inexact
+struct ExceptionFlags {
+  static constexpr uint16_t INVALID_F = 0x1;
+  // Some libcs define __FE_DENORM corresponding to the denormal input
+  // exception and include it in FE_ALL_EXCEPTS. We define and use it to
+  // support compiling against headers provided by such libcs.
+  static constexpr uint16_t DENORMAL_F = 0x2;
+  static constexpr uint16_t DIV_BY_ZERO_F = 0x4;
+  static constexpr uint16_t OVERFLOW_F = 0x8;
+  static constexpr uint16_t UNDERFLOW_F = 0x10;
+  static constexpr uint16_t INEXACT_F = 0x20;
+  static constexpr uint16_t ALL_F =
+      static_cast<uint16_t>(INVALID_F | DENORMAL_F | DIV_BY_ZERO_F |
+                            OVERFLOW_F | UNDERFLOW_F | INEXACT_F);
+  static constexpr unsigned MXCSR_EXCEPTION_MASK_BIT_POSITION = 7;
+};
+
+LIBC_INLINE static constexpr bool fenv_exceptions_match_x86() {
+  return (FE_INVALID == ExceptionFlags::INVALID_F) &&
+#ifdef __FE_DENORM
+         (__FE_DENORM == ExceptionFlags::DENORMAL_F) &&
+#elif defined(FE_DENORM)
+         (FE_DENORM == ExceptionFlags::DENORMAL_F) &&
+#endif // __FE_DENORM
+         (FE_DIVBYZERO == ExceptionFlags::DIV_BY_ZERO_F) &&
+         (FE_OVERFLOW == ExceptionFlags::OVERFLOW_F) &&
+         (FE_UNDERFLOW == ExceptionFlags::UNDERFLOW_F) &&
+         (FE_INEXACT == ExceptionFlags::INEXACT_F);
+}
+
+// The rounding control values in the x87 control register and the MXCSR
+// register have the same 2-bit enoding but have different bit positions.
+// See below for the bit positions.
+struct RoundingControl {
+  static constexpr uint16_t TO_NEAREST = 0x0;
+  static constexpr uint16_t DOWNWARD = 0x1;
+  static constexpr uint16_t UPWARD = 0x2;
+  static constexpr uint16_t TOWARD_ZERO = 0x3;
+  static constexpr uint16_t ROUNDING_MASK = 0x3;
+  static constexpr unsigned X87_BIT_POSITION = 10;
+  static constexpr unsigned MXCSR_BIT_POSITION = 13;
+  static constexpr uint16_t X87_ROUNDING_MASK = ROUNDING_MASK
+                                                << X87_BIT_POSITION;
+  static constexpr uint16_t MXCSR_ROUNDING_MASK = ROUNDING_MASK
+                                                  << MXCSR_BIT_POSITION;
+  static constexpr uint16_t ERROR = 0xFFFF;
+};
+
+static constexpr uint16_t MXCSR_ROUNDING_CONTROL_BIT_POSITION = 13;
+
+// Exception flags are individual bits in the corresponding registers.
+// So, we just OR the bit values to get the full set of exceptions.
+LIBC_INLINE static uint16_t get_status_value_from_except(int excepts) {
----------------
lntue wrote:

To answer your first question, the first one is yes, there are weird libc's, and unfortunately, an example is our own generated `fenv.h` header.  We will definitely need to revisit our `fenv.h` at some point.  OTOH, a bit flexibility on this part will allow users to switch between different the `fenv.h` headers seamlessly, like from their systems' to LLVM libc headers.

To your second and third point, yes, I'll need to migrate and unify all our internal C fenv usages (which are unfortunately spreading over several places.  And among one of those is to allow the internal functions to simply use the underlying libc's fenv implementation.  There are still reasons to carry our own custom implementations, such as:
- users use full build and only pick math functions but not fenv.h functions in the public package
- C fenv functions might be ignored by the compilers without `#pragma STDC FENV_ACCESS ON` and/or extra compilers' flags, and in general, they pessimize quite a lot of floating point optimizations.  OTOH, from what we've seen so far, inline asm and compiler builtins do quite well and still correct.

https://github.com/llvm/llvm-project/pull/165015


More information about the llvm-commits mailing list