[libcxx-commits] [libcxx] [libc++] Add a utility for checking the output of commands (PR #65917)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Sep 10 16:56:13 PDT 2023


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/65917:

This allows us to check the output of any tools.
For example, we can
- check for codegen properties - e.g. make sure `std::copy` with trivial types always calls `__builtin_memmove`
- ensure that the code gen of specific functions is always optimal - e.g. the cmp_* functions could be optimized as pointed out in #65136
- we can ensure that clang-tidy checks actually diagnose the things they should

These kinds of tests are more likely to break because of external tool changes, so they shouldn't be used if there is a different way to test things.

This tool is a strict subset of `FileCheck`in terms of it's functionality. I didn't just make that a dependency because it has quite heavy dependencies itself, which would make it by far the most costly part of building libc++. If we find that we extend this tool a lot (or FileCheck becomes less heavy) we should reconsider whether we should take the dependency hit.

>From d15dd81fe528b36575a141dbf6e091b1da8c8833 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 6 Sep 2023 08:31:36 -0700
Subject: [PATCH] [libc++] Add a utility for checking the output of commands

---
 libcxx/test/CMakeLists.txt                    |   2 +-
 libcxx/test/configs/cmake-bridge.cfg.in       |   1 +
 .../copy_move_codegen.sh.cpp                  |  21 +++
 .../clang_tidy/proper_version_checks.sh.cpp   |  29 ++++
 .../utility.intcmp/cmp_equal.codegen.sh.cpp   |  78 +++++++++++
 .../utility.intcmp/cmp_greater.codegen.sh.cpp |  78 +++++++++++
 .../cmp_greater_equal.codegen.sh.cpp          |  78 +++++++++++
 .../utility.intcmp/cmp_less.codegen.sh.cpp    |  78 +++++++++++
 .../cmp_less_equal.codegen.sh.cpp             |  78 +++++++++++
 .../cmp_not_equal.codegen.sh.cpp              |  78 +++++++++++
 .../utility.unreachable.codegen.sh.cpp        |  22 +++
 libcxx/test/tools/CMakeLists.txt              |   2 +
 libcxx/test/tools/check_output/CMakeLists.txt |  22 +++
 .../test/tools/check_output/check_output.cpp  | 131 ++++++++++++++++++
 .../proper_version_checks.cpp                 |   3 -
 15 files changed, 697 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/selftest/clang_tidy/proper_version_checks.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_equal.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater_equal.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less_equal.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_not_equal.codegen.sh.cpp
 create mode 100644 libcxx/test/libcxx/utilities/utility/utility.unreachable.codegen.sh.cpp
 create mode 100644 libcxx/test/tools/check_output/CMakeLists.txt
 create mode 100644 libcxx/test/tools/check_output/check_output.cpp

diff --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt
index c7036b94dcc596e..d10dd913a8c8d2f 100644
--- a/libcxx/test/CMakeLists.txt
+++ b/libcxx/test/CMakeLists.txt
@@ -84,7 +84,7 @@ if (LIBCXX_INCLUDE_TESTS)
     MAIN_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py")
 
   add_custom_target(cxx-test-depends
-    DEPENDS cxx ${LIBCXX_TEST_DEPS}
+    DEPENDS cxx check_output ${LIBCXX_TEST_DEPS}
     COMMENT "Builds dependencies required to run the test suite.")
 
   add_lit_testsuite(check-cxx
diff --git a/libcxx/test/configs/cmake-bridge.cfg.in b/libcxx/test/configs/cmake-bridge.cfg.in
index 9067115598abd8a..2b528d0b33c7d64 100644
--- a/libcxx/test/configs/cmake-bridge.cfg.in
+++ b/libcxx/test/configs/cmake-bridge.cfg.in
@@ -32,6 +32,7 @@ config.substitutions.append(('%{lib}', '@LIBCXX_LIBRARY_DIR@'))
 config.substitutions.append(('%{module}', '@LIBCXX_GENERATED_MODULE_DIR@'))
 config.substitutions.append(('%{executor}', '@LIBCXX_EXECUTOR@'))
 config.substitutions.append(('%{test-tools}', '@LIBCXX_TEST_TOOLS_PATH@'))
+config.substitutions.append(('%{check-output}', os.path.join('@CMAKE_BINARY_DIR@', 'bin/check_output') + " %s"))
 
 # The test needs to manually rebuild the module. The compiler flags used in the
 # test need to be the same as the compiler flags used to generate the module.
diff --git a/libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_codegen.sh.cpp b/libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_codegen.sh.cpp
new file mode 100644
index 000000000000000..86168b7f86752ce
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_codegen.sh.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <algorithm>
+
+int* test1(int* first, int* last, int* out) {
+  // CHECK:      define dso_local void
+  // CHECK-SAME: test
+  // CHECK:      tail call void @llvm.memmove
+  return std::copy(first, last, out);
+}
diff --git a/libcxx/test/libcxx/selftest/clang_tidy/proper_version_checks.sh.cpp b/libcxx/test/libcxx/selftest/clang_tidy/proper_version_checks.sh.cpp
new file mode 100644
index 000000000000000..cc3c90f4f0ea614
--- /dev/null
+++ b/libcxx/test/libcxx/selftest/clang_tidy/proper_version_checks.sh.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-clang-tidy
+
+// RUN: %{clang-tidy} %s -header-filter=.* --checks='-*,libcpp-cpp-version-check' --load=%{test-tools}/clang_tidy_checks/libcxx-tidy.plugin -- %{compile_flags} -fno-modules 2>&1 | %{check-output}
+
+#include <__config>
+
+// CHECK: warning: _LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version
+#if _LIBCPP_STD_VER > 14
+#endif
+
+// CHECK: warning: Use _LIBCPP_STD_VER instead of __cplusplus to constrain based on the C++ version
+#if __cplusplus >= 201103L
+#endif
+
+// CHECK: warning: _LIBCPP_STD_VER >= 11 is always true. Did you mean '#ifndef _LIBCPP_CXX03_LANG'?
+#if _LIBCPP_STD_VER >= 11
+#endif
+
+// CHECK: warning: Not a valid value for _LIBCPP_STD_VER. Use 14, 17, 20, 23, or 26
+#if _LIBCPP_STD_VER >= 12
+#endif
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_equal.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_equal.codegen.sh.cpp
new file mode 100644
index 000000000000000..e391b8b4df2558f
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_equal.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_equal_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_i16_i16
+  // CHECK-NEXT: %3 = icmp eq i16 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
+
+bool cmp_equal_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_i32_i32
+  // CHECK-NEXT: %3 = icmp eq i32 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
+
+bool cmp_equal_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_u32_i32
+  // CHECK-NEXT: %3 = icmp sgt i32 %1, -1
+  // CHECK-NEXT: %4 = icmp eq i32 %0, %1
+  // CHECK-NEXT: %5 = and i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
+
+bool cmp_equal_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_i32_u64
+  // CHECK-NEXT: %3 = icmp sgt i32 %0, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp eq i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
+
+bool cmp_equal_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_u32_i64
+  // CHECK-NEXT: %3 = icmp sgt i64 %1, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp eq i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
+
+bool cmp_equal_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_equal_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp eq i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_equal(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater.codegen.sh.cpp
new file mode 100644
index 000000000000000..a8eb5b9d69e354b
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_greater_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_i16_i16
+  // CHECK-NEXT: %3 = icmp slt i16 %1, %0
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
+
+bool cmp_greater_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_i32_i32
+  // CHECK-NEXT: %3 = icmp slt i32 %1, %0
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
+
+bool cmp_greater_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_u32_i32
+  // CHECK-NEXT: %3 = icmp slt i32 %1, 0
+  // CHECK-NEXT: %4 = icmp ult i32 %1, %0
+  // CHECK-NEXT: %5 = or i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
+
+bool cmp_greater_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_i32_u64
+  // CHECK-NEXT: %3 = icmp sgt i32 %0, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ugt i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
+
+bool cmp_greater_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_u32_i64
+  // CHECK-NEXT: %3 = icmp slt i64 %1, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ugt i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
+
+bool cmp_greater_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp ugt i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_greater(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater_equal.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater_equal.codegen.sh.cpp
new file mode 100644
index 000000000000000..d6b580c4908c758
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_greater_equal.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_greater_equal_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_i16_i16
+  // CHECK-NEXT: %3 = icmp sge i16 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
+
+bool cmp_greater_equal_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_i32_i32
+  // CHECK-NEXT: %3 = icmp sge i32 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
+
+bool cmp_greater_equal_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_u32_i32
+  // CHECK-NEXT: %3 = icmp slt i32 %1, 0
+  // CHECK-NEXT: %4 = icmp uge i32 %0, %1
+  // CHECK-NEXT: %5 = or i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
+
+bool cmp_greater_equal_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_i32_u64
+  // CHECK-NEXT: %3 = icmp sgt i32 %0, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp uge i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
+
+bool cmp_greater_equal_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_u32_i64
+  // CHECK-NEXT: %3 = icmp slt i64 %1, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp uge i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
+
+bool cmp_greater_equal_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_greater_equal_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp uge i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_greater_equal(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less.codegen.sh.cpp
new file mode 100644
index 000000000000000..fc4834aa1095931
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_less_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_i16_i16
+  // CHECK-NEXT: %3 = icmp slt i16 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
+
+bool cmp_less_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_i32_i32
+  // CHECK-NEXT: %3 = icmp slt i32 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
+
+bool cmp_less_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_u32_i32
+  // CHECK-NEXT: %3 = icmp sgt i32 %1, -1
+  // CHECK-NEXT: %4 = icmp ult i32 %0, %1
+  // CHECK-NEXT: %5 = and i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
+
+bool cmp_less_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_i32_u64
+  // CHECK-NEXT: %3 = icmp slt i32 %0, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ult i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
+
+bool cmp_less_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_u32_i64
+  // CHECK-NEXT: %3 = icmp sgt i64 %1, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ult i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
+
+bool cmp_less_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp ult i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_less(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less_equal.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less_equal.codegen.sh.cpp
new file mode 100644
index 000000000000000..bc4ca08023b5ef8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_less_equal.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_less_equal_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_i16_i16
+  // CHECK-NEXT: %3 = icmp sge i16 %1, %0
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
+
+bool cmp_less_equal_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_i32_i32
+  // CHECK-NEXT: %3 = icmp sge i32 %1, %0
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
+
+bool cmp_less_equal_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_u32_i32
+  // CHECK-NEXT: %3 = icmp sgt i32 %1, -1
+  // CHECK-NEXT: %4 = icmp uge i32 %1, %0
+  // CHECK-NEXT: %5 = and i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
+
+bool cmp_less_equal_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_i32_u64
+  // CHECK-NEXT: %3 = icmp slt i32 %0, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ule i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
+
+bool cmp_less_equal_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_u32_i64
+  // CHECK-NEXT: %3 = icmp sgt i64 %1, -1
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ule i64 %4, %1
+  // CHECK-NEXT: %6 = and i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
+
+bool cmp_less_equal_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_less_equal_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp ule i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_less_equal(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_not_equal.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_not_equal.codegen.sh.cpp
new file mode 100644
index 000000000000000..3a061a65fd9b75b
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.intcmp/cmp_not_equal.codegen.sh.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <cstdint>
+#include <utility>
+
+bool cmp_not_equal_i16_i16(int16_t lhs, int16_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_i16_i16
+  // CHECK-NEXT: %3 = icmp ne i16 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
+
+bool cmp_not_equal_i32_i32(int32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_i32_i32
+  // CHECK-NEXT: %3 = icmp ne i32 %0, %1
+  // CHECK-NEXT: ret i1 %3
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
+
+bool cmp_not_equal_u32_i32(uint32_t lhs, int32_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_u32_i32
+  // CHECK-NEXT: %3 = icmp slt i32 %1, 0
+  // CHECK-NEXT: %4 = icmp ne i32 %0, %1
+  // CHECK-NEXT: %5 = or i1 %3, %4
+  // CHECK-NEXT: ret i1 %5
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
+
+bool cmp_not_equal_i32_u64(int32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_i32_u64
+  // CHECK-NEXT: %3 = icmp slt i32 %0, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ne i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
+
+bool cmp_not_equal_u32_i64(uint32_t lhs, int64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_u32_i64
+  // CHECK-NEXT: %3 = icmp slt i64 %1, 0
+  // CHECK-NEXT: %4 = zext i32 %0 to i64
+  // CHECK-NEXT: %5 = icmp ne i64 %4, %1
+  // CHECK-NEXT: %6 = or i1 %3, %5
+  // CHECK-NEXT: ret i1 %6
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
+
+bool cmp_not_equal_u32_u64(uint32_t lhs, uint64_t rhs) {
+  // CHECK:      define dso_local noundef zeroext
+  // CHECK-SAME: cmp_not_equal_u32_u64
+  // CHECK-NEXT: %3 = zext i32 %0 to i64
+  // CHECK-NEXT: %4 = icmp ne i64 %3, %1
+  // CHECK-NEXT: ret i1 %4
+  // CHECK-NEXT: }
+  return std::cmp_not_equal(lhs, rhs);
+}
diff --git a/libcxx/test/libcxx/utilities/utility/utility.unreachable.codegen.sh.cpp b/libcxx/test/libcxx/utilities/utility/utility.unreachable.codegen.sh.cpp
new file mode 100644
index 000000000000000..c3a367763997f69
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/utility/utility.unreachable.codegen.sh.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This test is checking the LLVM IR
+// REQUIRES: clang
+
+// RUN: %{cxx} %s %{compile_flags} -O3 -c -S -emit-llvm -o - | %{check-output}
+
+#include <utility>
+
+[[noreturn]] void test() {
+  // CHECK:      define dso_local void
+  // CHECK-SAME: test
+  // CHECK-NEXT: unreachable
+  // CHECK-NEXT: }
+  std::unreachable();
+}
diff --git a/libcxx/test/tools/CMakeLists.txt b/libcxx/test/tools/CMakeLists.txt
index 10be63e8b50aa5a..ce86a9cf4067154 100644
--- a/libcxx/test/tools/CMakeLists.txt
+++ b/libcxx/test/tools/CMakeLists.txt
@@ -5,3 +5,5 @@ set(LIBCXX_TEST_TOOLS_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
 if(LIBCXX_ENABLE_CLANG_TIDY)
   add_subdirectory(clang_tidy_checks)
 endif()
+
+add_subdirectory(check_output)
diff --git a/libcxx/test/tools/check_output/CMakeLists.txt b/libcxx/test/tools/check_output/CMakeLists.txt
new file mode 100644
index 000000000000000..e1fe1a018e00e79
--- /dev/null
+++ b/libcxx/test/tools/check_output/CMakeLists.txt
@@ -0,0 +1,22 @@
+
+add_executable(check_output check_output.cpp)
+
+# Link against libc++
+if (TARGET cxx_shared)
+  target_link_libraries(check_output PRIVATE cxx_shared)
+elseif (TARGET cxx_static)
+  target_link_libraries(check_output PRIVATE cxx_static)
+else()
+  message(FATAL "Neither cxx_shared nor cxx_static are available to be linked against")
+endif()
+
+# put the binary into <build>/bin
+set_target_properties(check_output PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+
+# set the language mode
+set_target_properties(check_output PROPERTIES
+                      CXX_STANDARD 23
+                      CXX_STANDARD_REQUIRED YES
+                      CXX_EXTENSIONS NO)
+
+target_compile_options(check_output PRIVATE -Werror=missing-prototypes)
diff --git a/libcxx/test/tools/check_output/check_output.cpp b/libcxx/test/tools/check_output/check_output.cpp
new file mode 100644
index 000000000000000..36c6944c2d33e9d
--- /dev/null
+++ b/libcxx/test/tools/check_output/check_output.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <iterator>
+#include <print>
+#include <ranges>
+#include <string>
+
+using namespace std::string_view_literals;
+
+enum class Result {
+  success,
+  mismatch,
+  no_match_found,
+  unknown_matcher,
+  invalid_use,
+};
+
+namespace co {
+namespace {
+[[noreturn]] void exit(Result result) { std::exit(static_cast<int>(result)); }
+
+[[noreturn]] void print_failure(int line, std::string_view stdin_content, std::string_view matcher) {
+  std::print("Failed to match: `{}`\nRemaining data:\n{}", matcher, stdin_content);
+  co::exit(Result::mismatch);
+}
+
+bool is_newline(char c) { return c == '\n'; }
+
+bool isblank(char c) { return std::isblank(c); }
+
+bool consume_front(std::string_view& sv, std::string_view start) {
+  if (!sv.starts_with(start))
+    return false;
+  sv.remove_prefix(start.size());
+  return true;
+}
+} // namespace
+} // namespace co
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    std::print(stderr, "check_output has to be used as `<command> | ./check_output %s`\n");
+    co::exit(Result::invalid_use);
+  }
+
+  std::string file_content_data = [&] {
+    std::ifstream file(argv[1]);
+    if (!file) {
+      std::print(stderr, "Failed to open file: {}\n", argv[1]);
+      co::exit(Result::invalid_use);
+    }
+    return std::string{std::istreambuf_iterator<char>{file}, {}};
+  }();
+  std::string_view file_content = file_content_data; // Don't copy the data around all the time
+
+  std::string stdin_content_data = [&] {
+    std::cin >> std::noskipws;
+    return std::string{std::istream_iterator<char>{std::cin}, {}};
+  }();
+  std::string_view stdin_content = stdin_content_data; // Don't copy the data around all the time
+
+  size_t match_count = 0;
+  auto drop_blanks   = std::views::drop_while(co::isblank);
+
+  while (!file_content.empty()) {
+    auto marker = std::ranges::search(file_content, "// CHECK"sv);
+    if (marker.empty()) {
+      if (match_count == 0) {
+        std::print(stderr, "No matcher found!\n");
+        co::exit(Result::no_match_found);
+      }
+      co::exit(Result::success);
+    }
+    file_content.remove_prefix(marker.end() - file_content.begin());
+
+    const auto get_match = [&]() {
+      return std::string_view(file_content.begin(), std::ranges::find(file_content, '\n'));
+    };
+
+    if (co::consume_front(file_content, ":")) {
+      auto match = get_match();
+      auto found = std::ranges::search(
+          stdin_content | std::views::drop_while(std::not_fn(co::is_newline)) | std::views::drop(1),
+          match | drop_blanks);
+      if (found.empty()) {
+        co::print_failure(1, stdin_content, match);
+      }
+      ++match_count;
+      stdin_content.remove_prefix(found.end() - stdin_content.begin());
+    } else if (co::consume_front(file_content, "-SAME:")) {
+      auto match    = get_match();
+      auto haystack = std::string_view(stdin_content.begin(), std::ranges::find(stdin_content, '\n'));
+      auto found    = std::ranges::search(haystack, match | drop_blanks);
+      if (found.empty()) {
+        co::print_failure(1, stdin_content, match);
+      }
+      stdin_content.remove_prefix(found.end() - stdin_content.begin());
+    } else if (co::consume_front(file_content, "-NEXT:")) {
+      auto match    = get_match();
+      auto haystack = [&] {
+        auto begin = std::ranges::find(stdin_content, '\n');
+        if (begin == stdin_content.end()) {
+          co::print_failure(1, stdin_content, match);
+        }
+        ++begin;
+        return std::string_view(begin, std::ranges::find(begin, stdin_content.end(), '\n'));
+      }();
+      auto found = std::ranges::search(haystack, match | drop_blanks);
+      if (found.empty())
+        co::print_failure(1, stdin_content, match);
+      stdin_content.remove_prefix(found.end() - stdin_content.begin());
+    } else {
+      std::print(stderr,
+                 "Unkown matcher type {} found",
+                 std::string_view(file_content.begin(), std::ranges::find(file_content, ':')));
+      co::exit(Result::unknown_matcher);
+    }
+  }
+
+  co::exit(Result::success);
+}
diff --git a/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp
index 15093a4357bb7d1..1c4de838f218763 100644
--- a/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp
+++ b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp
@@ -36,9 +36,6 @@ class proper_version_checks_callbacks : public clang::PPCallbacks {
         clang::CharSourceRange::getTokenRange(condition_range),
         preprocessor_.getSourceManager(),
         preprocessor_.getLangOpts());
-    if (preprocessor_.getSourceManager().isInMainFile(location))
-      return;
-
     if (condition.starts_with("_LIBCPP_STD_VER") && condition.find(">") != std::string_view::npos &&
         condition.find(">=") == std::string_view::npos)
       check_.diag(location, "_LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version");



More information about the libcxx-commits mailing list