Skip to content

[libc] Prevent constant propagation for atanf(+-Inf) in gcc. #85733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 19, 2024

Conversation

lntue
Copy link
Contributor

@lntue lntue commented Mar 19, 2024

@llvmbot
Copy link
Member

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-libc

Author: None (lntue)

Changes

gcc bot failures with atanf(+-Inf): https://lab.llvm.org/buildbot/#/builders/250/builds/20331/steps/8/logs/stdio


Full diff: https://github.com/llvm/llvm-project/pull/85733.diff

1 Files Affected:

  • (modified) libc/src/math/generic/atanf.cpp (+4-2)
diff --git a/libc/src/math/generic/atanf.cpp b/libc/src/math/generic/atanf.cpp
index 69fd45ddd767e5..5f66ea52d0d7ae 100644
--- a/libc/src/math/generic/atanf.cpp
+++ b/libc/src/math/generic/atanf.cpp
@@ -37,8 +37,10 @@ LLVM_LIBC_FUNCTION(float, atanf, (float x)) {
     double const_term = 0.0;
     if (LIBC_UNLIKELY(x_abs >= 0x4180'0000)) {
       // atan(+-Inf) = +-pi/2.
-      if (x_bits.is_inf())
-        return static_cast<float>(SIGNED_PI_OVER_2[sign.is_neg()]);
+      if (x_bits.is_inf()) {
+        volatile double sign_pi_over_2 = SIGNED_PI_OVER_2[sign.is_neg()];
+        return static_cast<float>(sign_pi_over_2);
+      }
       if (x_bits.is_nan())
         return x;
       // x >= 16

@SchrodingerZhu
Copy link
Contributor

A side question, what should be the best way to mark a value as nondeterministic (non constant)?

@lntue
Copy link
Contributor Author

lntue commented Mar 19, 2024

A side question, what should be the best way to mark a value as nondeterministic (non constant)?

So far I found using volatile variables in the intermediate works well enough on both clang and gcc, especially on non-critical paths, since it might induce some performance penalty. For floating point computations, adding -frounding-math will prevent a lot of constant foldings of arithmetic operations. But that flag will be applied to the whole translation unit, and it might be a bit tricky/unpredictable when constexpr is also involved. There might be some other asm tricks, but it will most likely have to be platform-specific.

@lntue lntue merged commit a1fb514 into llvm:main Mar 19, 2024
@lntue lntue deleted the gcc branch March 19, 2024 15:22
@nickdesaulniers
Copy link
Member

Is there something pertaining to rounding modes at runtime resulting in different values than the rounding used during constant propagation?

@lntue
Copy link
Contributor Author

lntue commented Mar 19, 2024

Is there something pertaining to rounding modes at runtime resulting in different values than the rounding used during constant propagation?

As an example in this patch, casting double precision pi/2 to single precision results in different values depending on the rounding mode at runtime. So if that cast is constant folded, regardless of which rounding mode was chosen, the result will be wrong in some other rounding mode at runtime.

@nickdesaulniers
Copy link
Member

https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ also recommends the use of volatile. So does https://www.ibm.com/docs/en/xl-c-and-cpp-linux/11.1.0?topic=rounding-matching-compile-time-runtime-modes.

Looks like Hans Boehm wrote a paper on this issue (C++26): https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2746r0.pdf

There's an old clang PR that never landed for a pragma to adjust this behavior at compile time: https://reviews.llvm.org/D125625

You might want to speak with Hans and ask him what the current state of that proposal is.

Another approach, would it be possible to have another array similar to SIGNED_PI_OVER_2 but of floats with different values pre-rounded a certain way to avoid needing volatile? Or is it literally that calling atanf for the same input with different rounding modes is expected to produce different results?

@lntue
Copy link
Contributor Author

lntue commented Mar 19, 2024

https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ also recommends the use of volatile. So does https://www.ibm.com/docs/en/xl-c-and-cpp-linux/11.1.0?topic=rounding-matching-compile-time-runtime-modes.

Yes, we have similar utilities implemented for free-standing rounding mode detection: https://github.com/llvm/llvm-project/blob/main/libc/src/__support/FPUtil/rounding_mode.h

Looks like Hans Boehm wrote a paper on this issue (C++26): https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2746r0.pdf

From my understanding, Hans proposed to have a set of directed-rounding operations, and migrating people to use that instead of manipulating the rounding environment. Then fegetround and fesetround can be deprecated, and everything else can assume rounding-to-nearest by default. Until fegetround and fesetround are deprecated, we still have to respect the current rounding mode environment. And in fact, in the transition period, our implementation can be used to implement directed-rounding operation by wrapping fesetround before and after.

There's an old clang PR that never landed for a pragma to adjust this behavior at compile time: https://reviews.llvm.org/D125625

Yes, I saw that pragma is used in many places, but unfortunately it's not portable to clang.

You might want to speak with Hans and ask him what the current state of that proposal is.

+1

Another approach, would it be possible to have another array similar to SIGNED_PI_OVER_2 but of floats with different values pre-rounded a certain way to avoid needing volatile? Or is it literally that calling atanf for the same input with different rounding modes is expected to produce different results?

To have an array of pre-rounded results, we still need to access the current rounding mode at runtime, which leads to either fegetround heavy-weight or the same volatile usage in https://github.com/llvm/llvm-project/blob/main/libc/src/__support/FPUtil/rounding_mode.h

Another way to do it is to use fputil::round_result_slightly_up and fputil::round_result_slightly_down as in setting other exceptional values https://github.com/llvm/llvm-project/blob/main/libc/src/math/generic/atanf.cpp#L87, which is pretty much the same: https://github.com/llvm/llvm-project/blob/main/libc/src/__support/FPUtil/except_value_utils.h#L103

chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants