Skip to content

[libc++] Floating Point Atomic #67799

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 36 commits into from
Nov 22, 2023
Merged

[libc++] Floating Point Atomic #67799

merged 36 commits into from
Nov 22, 2023

Conversation

huixie90
Copy link
Member

  • implement P0020R6 Floating Point Atomic

Differential Revision: https://reviews.llvm.org/D153981

@github-actions
Copy link

github-actions bot commented Oct 11, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@github-actions
Copy link

github-actions bot commented Oct 16, 2023

✅ With the latest revision this PR passed the Python code formatter.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

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

LGTM once CI is passing and the temporary tests have been cleaned up. Thanks a lot for all the digging you had to do to make this work, it's really non-trivial! It would be great to get some attention on #47978, maybe we should ping the clang or compiler-rt folks there.

@huixie90 huixie90 marked this pull request as ready for review October 20, 2023 17:36
@huixie90 huixie90 requested a review from a team as a code owner October 20, 2023 17:36
@huixie90 huixie90 changed the title [libc++][In progress] Floating Point Atomic [libc++] Floating Point Atomic Oct 22, 2023
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

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

Let's ship this.

There are a few things that don't work especially with long double. However, this patch doesn't make the situation any worse than it already is, since people can already witness the broken behavior with e.g. atomic<long double>::load() today. I think this patch is in a really good shape and the fixes on top are going to be Clang or compiler-rt fixes.

@ldionne
Copy link
Member

ldionne commented Nov 22, 2023

@huixie90 it's landed breaking msan tsan even presubmit failed them "37 of 40 checks passed"

Hmm, I just looked and the msan timeouts are unrelated to this patch (we're having some trouble right now):

********************
Timed Out Tests (2):
  llvm-libc++-shared.cfg.in :: std/containers/sequences/deque/deque.modifiers/insert_iter_iter.pass.cpp
  llvm-libc++-shared.cfg.in :: std/numerics/rand/rand.dist/rand.dist.bern/rand.dist.bern.negbin/eval.pass.cpp

The tsan failures are more mysterious, but it looks like maybe the job as a whole timed out (which could be due to this patch or not): https://github.com/llvm/llvm-project/actions/runs/6933781778/job/18867566743?pr=67799

So I'm not certain @huixie90 bypassed any checks he should not have bypassed due to ongoing flakes as part of merging this. I'm not claiming this means those tests aren't broken on tsan/msan, I'm just saying the information we have with the checks on this patch doesn't allow claiming that.

@vitalybuka
Copy link
Collaborator

@huixie90 it's landed breaking msan tsan even presubmit failed them "37 of 40 checks passed"

Never mind, libcxx actions failed different tests, I have no idea if they are related.

@huixie90
Copy link
Member Author

Hi, I am a bit confused now. I looked at the UI under this tab and it was all green. (That was how I check if anything failed before). But It does seem that the UI changed a bit

@huixie90
Copy link
Member Author

Screenshot 2023-11-22 at 19 10 39

like this is where I checked. and things seem to be changed between my last push and previous push

ldionne pushed a commit that referenced this pull request Nov 22, 2023
This adds a few UNSUPPORTED annotations for tests that hang on some
msan bots:

https://lab.llvm.org/buildbot/#/builders/sanitizer-x86_64-linux-bootstrap-msan
https://lab.llvm.org/buildbot/#/builders/sanitizer-aarch64-linux-bootstrap-msan

We still haven't figured out the root cause of them hanging on these 
bots but not on the main libc++ CI infra.
@huixie90
Copy link
Member Author

Screenshot 2023-11-22 at 19 10 39

like this is where I checked. and things seem to be changed between my last push and previous push

@ldionne our CI used to be under this "show all checks" button but now it is gone

@vitalybuka
Copy link
Collaborator

I am not sure if these run before or after the merge
image

@petrhosek
Copy link
Member

We started seeing tests failures after this patch:
[...]

This is likely because our tests didn't reference that function before (our tests for atomics were very very naive). But that function should be provided by compiler-rt. Can you dig into it a bit more? This isn't something wrong with this patch per se, it's probably something wrong with the testing configuration on Fuchsia (which might be attributable to libc++'s config or not).

To be clear, this is a Fuchsia Clang toolchain, but the test is actually failing on Linux so it's not Fuchsia-specific. What's somewhat unusual for our toolchain is that we ship libclang_rt.atomic.so (that's part of compiler-rt) and there is no libatomic.so (that's part of GCC) available on our bots (because we want our toolchain to be fully hermetic). We already encountered an issue in compiler-rt tests which were linking libatomic.so unconditionally and we had to address that in https://reviews.llvm.org/D151680.

I see neither libatomic.so nor libclang_rt.atomic.so in the failing link command. Do you know how are those references satisfied in other toolchains?

@huixie90
Copy link
Member Author

We started seeing tests failures after this patch:
[...]

This is likely because our tests didn't reference that function before (our tests for atomics were very very naive). But that function should be provided by compiler-rt. Can you dig into it a bit more? This isn't something wrong with this patch per se, it's probably something wrong with the testing configuration on Fuchsia (which might be attributable to libc++'s config or not).

To be clear, this is a Fuchsia Clang toolchain, but the test is actually failing on Linux so it's not Fuchsia-specific. What's somewhat unusual for our toolchain is that we ship libclang_rt.atomic.so (that's part of compiler-rt) and there is no libatomic.so (that's part of GCC) available on our bots (because we want our toolchain to be fully hermetic). We already encountered an issue in compiler-rt tests which were linking libatomic.so unconditionally and we had to address that in https://reviews.llvm.org/D151680.

I see neither libatomic.so nor libclang_rt.atomic.so in the failing link command. Do you know how are those references satisfied in other toolchains?

Hello, the test is conditionally adding -latomic if compiling a dummy programme with -latomic return 0. In Libc++ CI, on Linux -latomic would be valid so it would add -latomic in the test. On Mac, -latomic would make the dummy program fail to compile/link , so it would not add the flag. But the test link fine without the flag.

I guess in Fushia, the -latomic dummy program test returned false , so it did not add the flag. What is the right way to link under fushia ?

@petrhosek
Copy link
Member

We started seeing tests failures after this patch:
[...]

This is likely because our tests didn't reference that function before (our tests for atomics were very very naive). But that function should be provided by compiler-rt. Can you dig into it a bit more? This isn't something wrong with this patch per se, it's probably something wrong with the testing configuration on Fuchsia (which might be attributable to libc++'s config or not).

To be clear, this is a Fuchsia Clang toolchain, but the test is actually failing on Linux so it's not Fuchsia-specific. What's somewhat unusual for our toolchain is that we ship libclang_rt.atomic.so (that's part of compiler-rt) and there is no libatomic.so (that's part of GCC) available on our bots (because we want our toolchain to be fully hermetic). We already encountered an issue in compiler-rt tests which were linking libatomic.so unconditionally and we had to address that in https://reviews.llvm.org/D151680.
I see neither libatomic.so nor libclang_rt.atomic.so in the failing link command. Do you know how are those references satisfied in other toolchains?

Hello, the test is conditionally adding -latomic if compiling a dummy programme with -latomic return 0. In Libc++ CI, on Linux -latomic would be valid so it would add -latomic in the test. On Mac, -latomic would make the dummy program fail to compile/link , so it would not add the flag. But the test link fine without the flag.

I guess in Fushia, the -latomic dummy program test returned false , so it did not add the flag. What is the right way to link under fushia ?

To emphasize again, we're seeing this issue on Linux and not on Fuchsia.

The existing has-latomic feature test is insufficient:

Feature(
name="has-latomic",
when=lambda cfg: sourceBuilds(
cfg,
"""
int main(int, char**) { return 0; }
""",
["-latomic"],
),
),

libatomic.so is part of GCC and this test will only succeed when you have GCC installed on your machine. We don't have GCC installed on our bots, we use Clang exclusively. We need a similar test for libclang_rt.atomic.so which is the compiler-rt counterpart.

@ldionne
Copy link
Member

ldionne commented Nov 22, 2023

Actually, is there a reason why we even need to link against any of these libraries manually? Why doesn't Clang link against compiler-rt automatically? It does that for other system libraries by default, why is the atomic support library treated differently?

ldionne pushed a commit that referenced this pull request Nov 23, 2023
Undo a part of #73152.

These test do not hang, but unexpectedlty pass on aarch64
https://lab.llvm.org/buildbot/#/builders/74/builds/23708

Tests work on aarch64 and probably any platform without fp80.
@ldionne
Copy link
Member

ldionne commented Nov 24, 2023

I think we got to the bottom of it with Hui just now. He's going to upload a patch soon that should address this issue. Note that these tests are going to be disabled on platforms like Fuchsia where we don't know how to get __atomic_store symbols & friends though

@mstorsjo
Copy link
Member

Just for clarity; the issue that @petrhosek has reported wasn’t about Fuchsia, it is the Fuchsia team’s toolchain build, targeting Linux - the issue lies in, AFAIK, what library to link in for atomics. But a fix that disables those tests if unable to link in working atomics probably is right in any case.

@mgorny
Copy link
Member

mgorny commented Nov 25, 2023

These tests are hanging on my Gentoo amd64 machine:

  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp

I've killed them after they were running at 100% CPU utilization for over 20 minutes.

Full output (after killing)
FAIL: llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp (9682 of 9685)
******************** TEST 'llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp' FAILED ********************
Exit Code: 241

Command Output (stdout):
--
# COMPILED WITH
/usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir/t.tmp.exe
# executed command: /usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir/t.tmp.exe
# EXECUTED AS
/usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir --  /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir/t.tmp.exe
# executed command: /usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir -- /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.minus_equals.pass.cpp.dir/t.tmp.exe
# note: command had no output on stdout or stderr
# error: command failed with exit status: 241

--

********************
FAIL: llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp (9683 of 9685)
******************** TEST 'llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp' FAILED ********************
Exit Code: 241

Command Output (stdout):
--
# COMPILED WITH
/usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir/t.tmp.exe
# executed command: /usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir/t.tmp.exe
# EXECUTED AS
/usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir --  /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir/t.tmp.exe
# executed command: /usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir -- /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_sub.pass.cpp.dir/t.tmp.exe
# note: command had no output on stdout or stderr
# error: command failed with exit status: 241

--

********************
FAIL: llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp (9684 of 9685)
******************** TEST 'llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp' FAILED ********************
Exit Code: 241

Command Output (stdout):
--
# COMPILED WITH
/usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir/t.tmp.exe
# executed command: /usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir/t.tmp.exe
# EXECUTED AS
/usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir --  /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir/t.tmp.exe
# executed command: /usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir -- /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/fetch_add.pass.cpp.dir/t.tmp.exe
# note: command had no output on stdout or stderr
# error: command failed with exit status: 241

--

********************
FAIL: llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp (9685 of 9685)
******************** TEST 'llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp' FAILED ********************
Exit Code: 241

Command Output (stdout):
--
# COMPILED WITH
/usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir/t.tmp.exe
# executed command: /usr/lib/ccache/bin/x86_64-pc-linux-gnu-clang++ /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp -pthread --target=x86_64-pc-linux-gnu -nostdinc++ -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/include/c++/v1 -I /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -latomic -lc++experimental -nostdlib++ -L /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -Wl,-rpath,/var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/lib -lc++ -o /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir/t.tmp.exe
# EXECUTED AS
/usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir --  /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir/t.tmp.exe
# executed command: /usr/bin/python3.12 /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/libcxx/utils/run.py --execdir /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir -- /var/tmp/portage/sys-libs/libcxx-18.0.0_pre20231123/work/runtimes_build-abi_x86_64.amd64/test/std/atomics/atomics.types.generic/atomics.types.float/Output/operator.plus_equals.pass.cpp.dir/t.tmp.exe
# note: command had no output on stdout or stderr
# error: command failed with exit status: 241

--

********************
********************
Failed Tests (4):
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp


Testing Time: 3378.31s

Total Discovered Tests: 9685
  Unsupported      :  571 (5.90%)
  Passed           : 9069 (93.64%)
  Expectedly Failed:   41 (0.42%)
  Failed           :    4 (0.04%)

zeroomega referenced this pull request Dec 1, 2023
We really shouldn't be depending on far away configuration options like
LLVM_HAVE_LINK_VERSION_SCRIPT here. This patch simplifies the enablement
of the linker scripts and as a result gets rid of an undesirable
dependency on HandleLLVMOptions.cmake.

As a drive-by, the patch also stops taking into account whether Python3
is available. This should have no bearing on whether we generate a
linker script or not, which is required for correctness. If someone
tries to build libc++ and generate a linker script but Python3 is not
available, they should get an error instead of silently getting an
incorrect installation of the library.
@androm3da
Copy link
Member

These tests are hanging on my Gentoo amd64 machine:

  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp
  llvm-libc++-shared.cfg.in :: std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp

I've killed them after they were running at 100% CPU utilization for over 20 minutes.

Can you get a stacktrace in the deadlock condition @mgorny ?

@ldionne
Copy link
Member

ldionne commented Dec 18, 2023

Note that I think this is "resolved" now. @huixie90 turned off the tests that were broken for long double and is working on fixing the underlying issues in Clang.

frederick-vs-ja added a commit that referenced this pull request Feb 19, 2025
The corresponding feature was implemented in LLVM 18 (by #67799), but
this FTM wasn't added before.
swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Feb 20, 2025
The corresponding feature was implemented in LLVM 18 (by llvm#67799), but
this FTM wasn't added before.

(cherry picked from commit 2207e3e)
@Endilll Endilll added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-libcxx

Author: Hui (huixie90)

Changes
  • implement P0020R6 Floating Point Atomic

Differential Revision: https://reviews.llvm.org/D153981


Patch is 87.53 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/67799.diff

26 Files Affected:

  • (modified) libcxx/docs/ReleaseNotes/18.rst (+1)
  • (modified) libcxx/docs/Status/Cxx20Papers.csv (+1-1)
  • (modified) libcxx/include/__atomic/atomic.h (+136)
  • (modified) libcxx/include/__atomic/cxx_atomic_impl.h (+47)
  • (modified) libcxx/include/atomic (+66)
  • (added) libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp (+51)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp (+62)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp (+226)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp (+244)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp (+29)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp (+68)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp (+77)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp (+118)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp (+119)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp (+140)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp (+61)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp (+98)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp (+82)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp (+58)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp (+102)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp (+102)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp (+114)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h (+122)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/types.compile.pass.cpp (+28)
  • (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp (+124)
  • (modified) libcxx/utils/libcxx/test/features.py (+10)
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 5f5ff83ca5512..c28a5b73f7ec6 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -49,6 +49,7 @@ Implemented Papers
 - P2614R2 - Deprecate ``numeric_limits::has_denorm``
 - P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
 - P2467R1 - Support exclusive mode for fstreams
+- P0020R6 - Floating Point Atomic
 
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 07f53ee995ea1..7aff860c68cf9 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -2,7 +2,7 @@
 "`P0463R1 <https://wg21.link/P0463R1>`__","LWG","Endian just Endian","Toronto","|Complete|","7.0"
 "`P0674R1 <https://wg21.link/P0674R1>`__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","15.0"
 "","","","","","",""
-"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","",""
+"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","|Complete|","18.0"
 "`P0053R7 <https://wg21.link/P0053R7>`__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","|Complete|","18.0"
 "`P0202R3 <https://wg21.link/P0202R3>`__","LWG","Add constexpr modifiers to functions in <algorithm> and <utility> Headers","Albuquerque","|Complete|","12.0"
 "`P0415R1 <https://wg21.link/P0415R1>`__","LWG","Constexpr for ``std::complex``\ ","Albuquerque","|Complete|","16.0"
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 47de6b958a96c..449802a2e3040 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -14,11 +14,17 @@
 #include <__atomic/cxx_atomic_impl.h>
 #include <__atomic/memory_order.h>
 #include <__config>
+#include <__functional/operations.h>
 #include <__memory/addressof.h>
+#include <__type_traits/is_floating_point.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/remove_const.h>
 #include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_volatile.h>
+#include <__utility/forward.h>
 #include <cstddef>
+#include <cstring>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -136,6 +142,136 @@ struct atomic<_Tp*>
     atomic& operator=(const atomic&) volatile = delete;
 };
 
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp>
+  requires is_floating_point_v<_Tp>
+struct atomic<_Tp> : __atomic_base<_Tp> {
+  private:
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __is_fp80_long_double() {
+        // Only x87-fp80 long double has 64-bit mantissa
+        return __LDBL_MANT_DIG__ == 64 && std::is_same_v<_Tp, long double>;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin() {
+#  ifndef _LIBCPP_COMPILER_CLANG_BASED
+        return false;
+#  else
+        // The builtin __cxx_atomic_fetch_add errors during compilation for
+        // long double on platforms with fp80 format.
+        // For more details, see
+        // lib/Sema/SemaChecking.cpp function IsAllowedValueType
+        // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+        // if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+        //    &Context.getTargetInfo().getLongDoubleFormat() ==
+        //        &llvm::APFloat::x87DoubleExtended())
+        // For more info
+        // https://github.com/llvm/llvm-project/issues/68602
+        // https://reviews.llvm.org/D53965
+        return !__is_fp80_long_double();
+#  endif
+    }
+
+    template <class _This, class _Operation, class _BuiltinOp>
+    _LIBCPP_HIDE_FROM_ABI static _Tp
+    __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _Operation __operation, _BuiltinOp __builtin_op) {
+        if constexpr (__has_rmw_builtin()) {
+          return __builtin_op(std::addressof(std::forward<_This>(__self).__a_), __operand, __m);
+        } else {
+          _Tp __old = __self.load(memory_order_relaxed);
+          _Tp __new = __operation(__old, __operand);
+          while (!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
+#  ifdef _LIBCPP_COMPILER_CLANG_BASED
+            if constexpr (__is_fp80_long_double()) {
+              // https://github.com/llvm/llvm-project/issues/47978
+              // clang bug: __old is not updated on failure for atomic<long double>::compare_exchange_weak
+              // Note __old = __self.load(memory_order_relaxed) will not work
+              std::__cxx_atomic_load_inplace(std::addressof(__self.__a_), &__old, memory_order_relaxed);
+            }
+#  endif
+            __new = __operation(__old, __operand);
+          }
+          return __old;
+        }
+    }
+
+    template <class _This>
+    _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_add(_This&& __self, _Tp __operand, memory_order __m) {
+        auto __builtin_op = [](auto __a, auto __builtin_operand, auto __order) {
+          return std::__cxx_atomic_fetch_add(__a, __builtin_operand, __order);
+        };
+        return __rmw_op(std::forward<_This>(__self), __operand, __m, std::plus<>{}, __builtin_op);
+    }
+
+    template <class _This>
+    _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_sub(_This&& __self, _Tp __operand, memory_order __m) {
+        auto __builtin_op = [](auto __a, auto __builtin_operand, auto __order) {
+          return std::__cxx_atomic_fetch_sub(__a, __builtin_operand, __order);
+        };
+        return __rmw_op(std::forward<_This>(__self), __operand, __m, std::minus<>{}, __builtin_op);
+    }
+
+  public:
+    using __base          = __atomic_base<_Tp>;
+    using value_type      = _Tp;
+    using difference_type = value_type;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr atomic(_Tp __d) noexcept : __base(__d) {}
+
+    atomic(const atomic&)                     = delete;
+    atomic& operator=(const atomic&)          = delete;
+    atomic& operator=(const atomic&) volatile = delete;
+
+    _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) volatile noexcept
+      requires __base::is_always_lock_free
+    {
+        __base::store(__d);
+        return __d;
+    }
+    _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) noexcept {
+        __base::store(__d);
+        return __d;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+      requires __base::is_always_lock_free
+    {
+        return __fetch_add(*this, __op, __m);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+        return __fetch_add(*this, __op, __m);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+      requires __base::is_always_lock_free
+    {
+        return __fetch_sub(*this, __op, __m);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+        return __fetch_sub(*this, __op, __m);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile noexcept
+      requires __base::is_always_lock_free
+    {
+        return fetch_add(__op) + __op;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) noexcept { return fetch_add(__op) + __op; }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile noexcept
+      requires __base::is_always_lock_free
+    {
+        return fetch_sub(__op) - __op;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept { return fetch_sub(__op) - __op; }
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
 // atomic_is_lock_free
 
 template <class _Tp>
diff --git a/libcxx/include/__atomic/cxx_atomic_impl.h b/libcxx/include/__atomic/cxx_atomic_impl.h
index d670fddc3934c..5d724669fee87 100644
--- a/libcxx/include/__atomic/cxx_atomic_impl.h
+++ b/libcxx/include/__atomic/cxx_atomic_impl.h
@@ -128,6 +128,18 @@ _Tp __cxx_atomic_load(const volatile __cxx_atomic_base_impl<_Tp>* __a,
   return __ret;
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(const volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
+  __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(const __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
+  __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+}
+
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI
 _Tp __cxx_atomic_load(const __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) {
@@ -362,6 +374,21 @@ _Tp __cxx_atomic_load(__cxx_atomic_base_impl<_Tp> const* __a, memory_order __ord
         const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
 }
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const volatile* __a, _Tp* __dst, memory_order __order) _NOEXCEPT {
+    using __ptr_type = __remove_const_t<decltype(__a->__a_value)>*;
+    *__dst           = __c11_atomic_load(
+        const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, memory_order __order) _NOEXCEPT {
+    using __ptr_type = __remove_const_t<decltype(__a->__a_value)>*;
+    *__dst           = __c11_atomic_load(
+        const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
+}
+
 template<class _Tp>
 _LIBCPP_HIDE_FROM_ABI
 _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
@@ -558,6 +585,16 @@ struct __cxx_atomic_lock_impl {
     __unlock();
     return __old;
   }
+  _LIBCPP_HIDE_FROM_ABI void __read_inplace(_Tp* __dst) const volatile {
+    __lock();
+    __cxx_atomic_assign_volatile(*__dst, __a_value);
+    __unlock();
+  }
+  _LIBCPP_HIDE_FROM_ABI void __read_inplace(_Tp* __dst) const {
+    __lock();
+    *__dst = __a_value;
+    __unlock();
+  }
 };
 
 template <typename _Tp>
@@ -597,6 +634,16 @@ _Tp __cxx_atomic_load(const __cxx_atomic_lock_impl<_Tp>* __a, memory_order) {
   return __a->__read();
 }
 
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load(const volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __dst, memory_order) {
+  __a->__read_inplace(__dst);
+}
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_load(const __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __dst, memory_order) {
+  __a->__read_inplace(__dst);
+}
+
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI
 _Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) {
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 2f122a707bdc3..7bed8fd8bacfc 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -262,6 +262,72 @@ struct atomic<T*>
     void notify_all() noexcept;
 };
 
+template<>
+struct atomic<floating-point-type> {  // since C++20
+  using value_type = floating-point-type;
+  using difference_type = value_type;
+
+  static constexpr bool is_always_lock_free = implementation-defined;
+  bool is_lock_free() const volatile noexcept;
+  bool is_lock_free() const noexcept;
+
+  constexpr atomic() noexcept;
+  constexpr atomic(floating-point-type) noexcept;
+  atomic(const atomic&) = delete;
+  atomic& operator=(const atomic&) = delete;
+  atomic& operator=(const atomic&) volatile = delete;
+
+  void store(floating-point-type, memory_order = memory_order::seq_cst) volatile noexcept;
+  void store(floating-point-type, memory_order = memory_order::seq_cst) noexcept;
+  floating-point-type operator=(floating-point-type) volatile noexcept;
+  floating-point-type operator=(floating-point-type) noexcept;
+  floating-point-type load(memory_order = memory_order::seq_cst) volatile noexcept;
+  floating-point-type load(memory_order = memory_order::seq_cst) noexcept;
+  operator floating-point-type() volatile noexcept;
+  operator floating-point-type() noexcept;
+
+  floating-point-type exchange(floating-point-type,
+                               memory_order = memory_order::seq_cst) volatile noexcept;
+  floating-point-type exchange(floating-point-type,
+                               memory_order = memory_order::seq_cst) noexcept;
+  bool compare_exchange_weak(floating-point-type&, floating-point-type,
+                             memory_order, memory_order) volatile noexcept;
+  bool compare_exchange_weak(floating-point-type&, floating-point-type,
+                             memory_order, memory_order) noexcept;
+  bool compare_exchange_strong(floating-point-type&, floating-point-type,
+                               memory_order, memory_order) volatile noexcept;
+  bool compare_exchange_strong(floating-point-type&, floating-point-type,
+                               memory_order, memory_order) noexcept;
+  bool compare_exchange_weak(floating-point-type&, floating-point-type,
+                             memory_order = memory_order::seq_cst) volatile noexcept;
+  bool compare_exchange_weak(floating-point-type&, floating-point-type,
+                             memory_order = memory_order::seq_cst) noexcept;
+  bool compare_exchange_strong(floating-point-type&, floating-point-type,
+                               memory_order = memory_order::seq_cst) volatile noexcept;
+  bool compare_exchange_strong(floating-point-type&, floating-point-type,
+                               memory_order = memory_order::seq_cst) noexcept;
+
+  floating-point-type fetch_add(floating-point-type,
+                                memory_order = memory_order::seq_cst) volatile noexcept;
+  floating-point-type fetch_add(floating-point-type,
+                                memory_order = memory_order::seq_cst) noexcept;
+  floating-point-type fetch_sub(floating-point-type,
+                                memory_order = memory_order::seq_cst) volatile noexcept;
+  floating-point-type fetch_sub(floating-point-type,
+                                memory_order = memory_order::seq_cst) noexcept;
+
+  floating-point-type operator+=(floating-point-type) volatile noexcept;
+  floating-point-type operator+=(floating-point-type) noexcept;
+  floating-point-type operator-=(floating-point-type) volatile noexcept;
+  floating-point-type operator-=(floating-point-type) noexcept;
+
+  void wait(floating-point-type, memory_order = memory_order::seq_cst) const volatile noexcept;
+  void wait(floating-point-type, memory_order = memory_order::seq_cst) const noexcept;
+  void notify_one() volatile noexcept;
+  void notify_one() noexcept;
+  void notify_all() volatile noexcept;
+  void notify_all() noexcept;
+};
 
 // [atomics.nonmembers], non-member functions
 template<class T>
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
new file mode 100644
index 0000000000000..46511530c7c75
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: target={{.+}}-windows-gnu
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+//   static constexpr bool is_always_lock_free = implementation-defined;
+//   bool is_lock_free() const volatile noexcept;
+//   bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+  //   static constexpr bool is_always_lock_free = implementation-defined;
+  {
+    bool r = std::atomic<T>::is_always_lock_free;
+    assert(r == __atomic_always_lock_free(sizeof(std::__cxx_atomic_impl<T>), 0));
+  }
+
+  //   bool is_lock_free() const volatile noexcept;
+  {
+    const volatile std::atomic<T> a;
+    bool r = a.is_lock_free();
+    assert(r == __cxx_atomic_is_lock_free(sizeof(std::__cxx_atomic_impl<T>)));
+  }
+
+  //   bool is_lock_free() const noexcept;
+  {
+    const std::atomic<T> a;
+    bool r = a.is_lock_free();
+    assert(r == __cxx_atomic_is_lock_free(sizeof(std::__cxx_atomic_impl<T>)));
+  }
+}
+
+int main(int, char**) {
+  test<float>();
+  test<double>();
+  test<long double>();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
new file mode 100644
index 0000000000000..8efb556cb5d99
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: target={{.+}}-windows-gnu
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+//  floating-point-type operator=(floating-point-type) volatile noexcept;
+//  floating-point-type operator=(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileAssign = requires(volatile std::atomic<T>& a, T t) { a = t; };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+  static_assert(HasVolatileAssign<T> == std::atomic<T>::is_always_lock_free);
+
+  static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() = (T(0))));
+
+  // assignment
+  {
+    MaybeVolatile<std::atomic<T>> a(3.1);
+    std::same_as<T> decltype(auto) r = (a = T(1.2));
+    assert(a.load() == T(1.2));
+    assert(r == T(1.2));
+  }
+
+  // memory_order::seq_cst
+  {
+    auto assign = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x = new_val; };
+    auto load   = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+    test_seq_cst<T, MaybeVolatile>(assign, load);
+  }
+}
+
+template <class T>
+void test() {
+  test_impl<T>();
+  if constexpr (std::atomic<T>::is_always_lock_free) {
+    test_impl<T, std::add_volatile_t>();
+  }
+}
+
+int main(int, char**) {
+  test<float>();
+  test<double>();
+  test<long double>();
+
+  return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
new file mode 100644
index 0000000000000..839d79d3a4110
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
@@ -0,0 +1,226 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+// bool compare_exchange_strong(T& expected, T desired,
+//                            memory_order success, memory_order failure) volatile noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+//                            memory_order success, memory_order failure) noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+//                            memory...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants