Skip to content

[libc][math] Add Generic Comparison Operations for floating point types #144983

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

Open
wants to merge 2,658 commits into
base: main
Choose a base branch
from

Conversation

krishna2803
Copy link
Contributor

@krishna2803 krishna2803 commented Jun 20, 2025

The PR implements the following generic comparison operation functions for floating point types along with unittests:

  • fputil::equals
  • fputil::less_than
  • fputil::less_than_or_equals
  • fputil::greater_than
  • fputil::greater_than_or_equals

cc @lntue @overmighty

@llvmbot llvmbot added the libc label Jun 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 20, 2025

@llvm/pr-subscribers-libc

Author: Krishna Pandey (krishna2803)

Changes

The PR implements the following generic comparison operation functions for floating point types along with unittests:

  • fputil::equals
  • fputil::not_equals
  • fputil::less_than
  • fputil::less_than_or_equals
  • fputil::greater_than
  • fputil::greater_than_or_equals

cc @lntue @overmighty


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

4 Files Affected:

  • (modified) libc/src/__support/FPUtil/CMakeLists.txt (+11)
  • (added) libc/src/__support/FPUtil/ComparisonOperations.h (+122)
  • (modified) libc/test/src/__support/FPUtil/CMakeLists.txt (+12)
  • (added) libc/test/src/__support/FPUtil/comparison_operations_test.cpp (+254)
diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt
index bfe0170f09fd9..ce068df8d4c03 100644
--- a/libc/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/src/__support/FPUtil/CMakeLists.txt
@@ -209,6 +209,17 @@ add_header_library(
     libc.src.__support.macros.properties.types
 )
 
+add_header_library(
+  comparison_operations
+  HDRS
+    ComparisonOperations.h
+  DEPENDS
+    .fp_bits
+    .fenv_impl
+    libc.src.__support.CPP.type_traits
+    libc.src.__support.config
+)
+
 add_header_library(
   hypot
   HDRS
diff --git a/libc/src/__support/FPUtil/ComparisonOperations.h b/libc/src/__support/FPUtil/ComparisonOperations.h
new file mode 100644
index 0000000000000..cf71a14df0465
--- /dev/null
+++ b/libc/src/__support/FPUtil/ComparisonOperations.h
@@ -0,0 +1,122 @@
+//===-- Comparison operations on floating point numbers --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H
+
+#include "FEnvImpl.h"                      // raise_except_if_required
+#include "FPBits.h"                        // FPBits<T>
+#include "src/__support/CPP/type_traits.h" // enable_if, is_floating_point
+#include "src/__support/macros/config.h"   // LIBC_NAMESPACE_DECL
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+// IEEE Standard 754-2019. Section 5.11
+// Rules for comparison within the same floating point type
+// 1. +0 = −0
+// 2. (i)   +inf  = +inf
+//    (ii)  -inf  = -inf
+//    (iii) -inf != +inf
+// 3. Any comparison with NaN return false except (NaN != NaN => true)
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> equals(T x,
+                                                                       T y) {
+  using FPBits = FPBits<T>;
+  FPBits x_bits(x);
+  FPBits y_bits(y);
+
+  if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
+    fputil::raise_except_if_required(FE_INVALID);
+
+  // NaN == x returns false for every x
+  if (x_bits.is_nan() || y_bits.is_nan())
+    return false;
+
+  // +/- 0 == +/- 0
+  if (x_bits.is_zero() && y_bits.is_zero())
+    return true;
+
+  // should also work for comparisons of different signs
+  return x_bits.uintval() == y_bits.uintval();
+}
+
+// !(x == y) => x != y
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
+not_equals(T x, T y) {
+  return !equals(x, y);
+}
+
+// Rules:
+// 1. -inf < x (x != -inf)
+// 2. x < +inf (x != +inf)
+// 3. Any comparison with NaN return false
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> less_than(T x,
+                                                                          T y) {
+  using FPBits = FPBits<T>;
+  FPBits x_bits(x);
+  FPBits y_bits(y);
+
+  if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
+    fputil::raise_except_if_required(FE_INVALID);
+
+  // Any comparison with NaN returns false
+  if (x_bits.is_nan() || y_bits.is_nan())
+    return false;
+
+  if (x_bits.is_zero() && y_bits.is_zero())
+    return false;
+
+  if (x_bits.is_neg() && y_bits.is_pos())
+    return true;
+
+  if (x_bits.is_pos() && y_bits.is_neg())
+    return false;
+
+  // since we store the float in the format: s | e | m
+  // the comparisons should work if we directly compare the uintval's
+
+  // TODO: verify if we should use FPBits.get_exponent and FPBits.get_mantissa
+  // instead of directly comparing uintval's
+
+  // both negative
+  if (x_bits.is_neg())
+    return x_bits.uintval() > y_bits.uintval();
+
+  // both positive
+  return x_bits.uintval() < y_bits.uintval();
+}
+
+// x < y => y > x
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
+greater_than(T x, T y) {
+  return less_than(y, x);
+}
+
+// following is expression is correct, accounting for NaN case(s) as well
+// x <= y => (x < y) || (x == y)
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
+less_than_or_equals(T x, T y) {
+  return less_than(x, y) || equals(x, y);
+}
+
+// x >= y => (x > y) || (x == y) => (y < x) || (x == y)
+template <typename T>
+LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
+greater_than_or_equals(T x, T y) {
+  return less_than(y, x) || equals(x, y);
+}
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H
diff --git a/libc/test/src/__support/FPUtil/CMakeLists.txt b/libc/test/src/__support/FPUtil/CMakeLists.txt
index 1e64e9ba425a5..1dcd666ec3d25 100644
--- a/libc/test/src/__support/FPUtil/CMakeLists.txt
+++ b/libc/test/src/__support/FPUtil/CMakeLists.txt
@@ -38,3 +38,15 @@ add_fp_unittest(
   DEPENDS
     libc.src.__support.FPUtil.rounding_mode
 )
+
+add_fp_unittest(
+  comparison_operations_test
+  SUITE
+    libc-fputil-tests
+  SRCS
+    comparison_operations_test.cpp
+  DEPENDS
+    libc.src.__support.FPUtil.fp_bits
+    # libc.src.__support.FPUtil.comparison_operations
+)
+
diff --git a/libc/test/src/__support/FPUtil/comparison_operations_test.cpp b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp
new file mode 100644
index 0000000000000..94c234446ad8e
--- /dev/null
+++ b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp
@@ -0,0 +1,254 @@
+//===-- Unittests for Comparison Operations for FPBits class -------------===//
+//
+// 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 "src/__support/FPUtil/ComparisonOperations.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/properties/types.h"
+#include "src/__support/sign.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LIBC_NAMESPACE::fputil::equals;
+using LIBC_NAMESPACE::fputil::greater_than;
+using LIBC_NAMESPACE::fputil::greater_than_or_equals;
+using LIBC_NAMESPACE::fputil::less_than;
+using LIBC_NAMESPACE::fputil::less_than_or_equals;
+using LIBC_NAMESPACE::fputil::not_equals;
+
+// FIXME: currently i have used NAN here
+// need to find a better way to get a NAN floating point type
+// - need to see if FPRep could be used?
+
+#define TEST_EQUALS(Name, Type)                                                \
+  TEST(LlvmLibc##Name##ComparisionOperationsTest, Equals) {                                   \
+    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
+    Type pos_zero = Bits::zero().get_val();                                    \
+    Type neg_zero = -pos_zero;                                                 \
+    Type pos_inf = Bits::inf().get_val();                                      \
+    Type neg_inf = Bits::inf(Sign::NEG).get_val();                             \
+    Type nan = NAN;                                                            \
+    Type pos_normal = Type(3.14);                                              \
+    Type neg_normal = Type(-2.71);                                             \
+    Type pos_large = Type(1000000.0);                                          \
+    Type neg_large = Type(-1000000.0);                                         \
+                                                                               \
+    EXPECT_TRUE(equals(pos_zero, pos_zero));                                   \
+    EXPECT_TRUE(equals(neg_zero, neg_zero));                                   \
+    EXPECT_TRUE(equals(pos_inf, pos_inf));                                     \
+    EXPECT_TRUE(equals(neg_inf, neg_inf));                                     \
+    EXPECT_TRUE(equals(pos_normal, pos_normal));                               \
+    EXPECT_TRUE(equals(neg_normal, neg_normal));                               \
+                                                                               \
+    EXPECT_TRUE(equals(pos_zero, neg_zero));                                   \
+    EXPECT_TRUE(equals(neg_zero, pos_zero));                                   \
+                                                                               \
+    EXPECT_FALSE(equals(pos_normal, neg_normal));                              \
+    EXPECT_FALSE(equals(pos_normal, pos_large));                               \
+    EXPECT_FALSE(equals(pos_inf, neg_inf));                                    \
+    EXPECT_FALSE(equals(pos_inf, pos_normal));                                 \
+    EXPECT_FALSE(equals(neg_inf, neg_normal));                                 \
+    EXPECT_FALSE(equals(pos_large, neg_large));                                \
+                                                                               \
+    EXPECT_FALSE(equals(nan, nan));                                            \
+    EXPECT_FALSE(equals(nan, pos_normal));                                     \
+    EXPECT_FALSE(equals(nan, pos_zero));                                       \
+    EXPECT_FALSE(equals(nan, pos_inf));                                        \
+    EXPECT_FALSE(equals(pos_normal, nan));                                     \
+  }
+
+#define TEST_NOT_EQUALS(Name, Type)                                            \
+  TEST(LlvmLibc##Name##ComparisionOperationsTest, NotEquals) {                                \
+    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
+    Type pos_zero = Bits::zero().get_val();                                    \
+    Type neg_zero = Bits::zero(Sign::NEG).get_val();                           \
+    Type pos_inf = Bits::inf().get_val();                                      \
+    Type neg_inf = Bits::inf(Sign::NEG).get_val();                             \
+    Type nan = NAN;                                                            \
+    Type pos_normal = Type(3.14);                                              \
+    Type neg_normal = Type(-2.71);                                             \
+    Type pos_large = Type(1000000.0);                                          \
+    Type neg_large = Type(-1000000.0);                                         \
+                                                                               \
+    EXPECT_FALSE(not_equals(pos_zero, pos_zero));                              \
+    EXPECT_FALSE(not_equals(pos_zero, neg_zero));                              \
+    EXPECT_FALSE(not_equals(pos_inf, pos_inf));                                \
+    EXPECT_FALSE(not_equals(neg_inf, neg_inf));                                \
+    EXPECT_FALSE(not_equals(pos_normal, pos_normal));                          \
+                                                                               \
+    EXPECT_TRUE(not_equals(pos_normal, neg_normal));                           \
+    EXPECT_TRUE(not_equals(pos_inf, neg_inf));                                 \
+    EXPECT_TRUE(not_equals(pos_normal, pos_zero));                             \
+    EXPECT_TRUE(not_equals(pos_large, neg_large));                             \
+    EXPECT_TRUE(not_equals(pos_inf, pos_normal));                              \
+                                                                               \
+    EXPECT_TRUE(not_equals(nan, nan));                                         \
+    EXPECT_TRUE(not_equals(nan, pos_normal));                                  \
+    EXPECT_TRUE(not_equals(nan, pos_zero));                                    \
+    EXPECT_TRUE(not_equals(nan, pos_inf));                                     \
+    EXPECT_TRUE(not_equals(pos_normal, nan));                                  \
+  }
+
+#define TEST_LESS_THAN(Name, Type)                                             \
+  TEST(LlvmLibc##Name##ComparisionOperationsTest, LessThan) {                                 \
+    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
+    Type pos_zero = Bits::zero().get_val();                                    \
+    Type neg_zero = -pos_zero;                                                 \
+    Type pos_inf = Bits::inf().get_val();                                      \
+    Type neg_inf = Bits::inf(Sign::NEG).get_val();                             \
+    Type nan = NAN;                                                            \
+    Type pos_small = Type(0.1);                                                \
+    Type neg_small = Type(-0.1);                                               \
+    Type pos_large = Type(1000000.0);                                          \
+    Type neg_large = Type(-1000000.0);                                         \
+                                                                               \
+    EXPECT_TRUE(less_than(neg_small, pos_small));                              \
+    EXPECT_TRUE(less_than(pos_small, pos_large));                              \
+    EXPECT_TRUE(less_than(neg_large, neg_small));                              \
+    EXPECT_FALSE(less_than(pos_large, pos_small));                             \
+    EXPECT_FALSE(less_than(pos_small, neg_small));                             \
+                                                                               \
+    EXPECT_FALSE(less_than(pos_zero, neg_zero));                               \
+    EXPECT_FALSE(less_than(neg_zero, pos_zero));                               \
+    EXPECT_FALSE(less_than(pos_zero, pos_zero));                               \
+                                                                               \
+    EXPECT_TRUE(less_than(neg_small, pos_zero));                               \
+    EXPECT_TRUE(less_than(neg_zero, pos_small));                               \
+    EXPECT_FALSE(less_than(pos_small, pos_zero));                              \
+                                                                               \
+    EXPECT_TRUE(less_than(neg_inf, pos_inf));                                  \
+    EXPECT_TRUE(less_than(neg_inf, neg_small));                                \
+    EXPECT_TRUE(less_than(pos_small, pos_inf));                                \
+    EXPECT_FALSE(less_than(pos_inf, pos_small));                               \
+                                                                               \
+    EXPECT_FALSE(less_than(pos_small, pos_small));                             \
+    EXPECT_FALSE(less_than(neg_inf, neg_inf));                                 \
+                                                                               \
+    EXPECT_FALSE(less_than(nan, pos_small));                                   \
+    EXPECT_FALSE(less_than(pos_small, nan));                                   \
+    EXPECT_FALSE(less_than(nan, nan));                                         \
+  }
+
+#define TEST_GREATER_THAN(Name, Type)                                          \
+  TEST(LlvmLibc##Name##ComparisionOperationsTest, GreaterThan) {                              \
+    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
+    Type pos_zero = Bits::zero().get_val();                                    \
+    Type neg_zero = -pos_zero;                                                 \
+    Type pos_inf = Bits::inf().get_val();                                      \
+    Type neg_inf = Bits::inf(Sign::NEG).get_val();                             \
+    Type nan = NAN;                                                            \
+    Type pos_small = Type(0.1);                                                \
+    Type neg_small = Type(-0.1);                                               \
+    Type pos_large = Type(1000000.0);                                          \
+    Type neg_large = Type(-1000000.0);                                         \
+                                                                               \
+    EXPECT_TRUE(greater_than(pos_small, neg_small));                           \
+    EXPECT_TRUE(greater_than(pos_large, pos_small));                           \
+    EXPECT_TRUE(greater_than(neg_small, neg_large));                           \
+    EXPECT_FALSE(greater_than(pos_small, pos_large));                          \
+    EXPECT_FALSE(greater_than(neg_small, pos_small));                          \
+                                                                               \
+    EXPECT_FALSE(greater_than(pos_zero, neg_zero));                            \
+    EXPECT_FALSE(greater_than(neg_zero, pos_zero));                            \
+                                                                               \
+    EXPECT_TRUE(greater_than(pos_inf, neg_inf));                               \
+    EXPECT_TRUE(greater_than(pos_inf, pos_small));                             \
+    EXPECT_TRUE(greater_than(pos_small, neg_inf));                             \
+    EXPECT_FALSE(greater_than(neg_inf, pos_inf));                              \
+                                                                               \
+    EXPECT_FALSE(greater_than(pos_small, pos_small));                          \
+    EXPECT_FALSE(greater_than(pos_inf, pos_inf));                              \
+                                                                               \
+    EXPECT_FALSE(greater_than(nan, pos_small));                                \
+    EXPECT_FALSE(greater_than(pos_small, nan));                                \
+    EXPECT_FALSE(greater_than(nan, nan));                                      \
+  }
+
+#define TEST_LESS_THAN_OR_EQUALS(Name, Type)                                   \
+  TEST(LlvmLibc##Name##ComparisionOperationsTest, LessThanOrEquals) {                         \
+    using Bits = LIBC_NAMESPACE::fputil::FPBits<Type>;                         \
+    Type pos_zero = Bits::zero().get_val();                                    \
+    Type neg_zero = -pos_zero;                                                 \
+    Type pos_inf = Bits::inf().get_val();                                      \
+    Type neg_inf = Bits::inf(Sign::NEG).get_val();                             \
+    Type nan = NAN;                                                            \
+    Type pos_small = Type(0.1);                                                \
+    Type neg_small = Type(-0.1);                                               \
+    Type pos_large = Type(1000000.0);                                          \
+    Type neg_large = Type(-1000000.0);                                         \
+                                                                               \
+    EXPECT_TRUE(less_than_or_equals(neg_small, pos_small));                    \
+    EXPECT_TRUE(less_than_or_equals(pos_small, pos_large));                    \
+    EXPECT_TRUE(less_than_or_equals(neg_inf, pos_small));                      \
+                                                                               \
+    EXPECT_TRUE(less_than_or_equals(pos_small, pos_small));                    \
+    EXPECT_TRUE(less_than_or_equals(pos_zero, neg_zero));                      \
+    EXPECT_TRUE(less_than_or_equals(pos_inf, pos_inf));                        \
+                                                                               \
+    EXPECT_FALSE(less_than_or_equals(pos_small, neg_small));                   \
+    EXPECT_FALSE(less_than_or_equals(pos_large, pos_small));                   \
+    EXPECT_FALSE(less_than_or_equals(pos_inf, pos_small));                     \
+                                                                               \
+    EXPECT_TRUE(less_than_or_equals(neg_large, pos_small));                    \
+    EXPECT_FALSE(less_than_or_equals(pos_large, neg_small));                   \
+                                                                               \
+    EXPECT_FALSE(less_than_or_equals(nan, pos_small));                         \
+    EXPECT_FALSE(less_than_or_equals(pos_small, nan));                         \
+    EXPECT_FALSE(l...
[truncated]

@overmighty overmighty requested review from lntue and overmighty June 20, 2025 09:08
@krishna2803 krishna2803 requested a review from overmighty July 4, 2025 14:19
@@ -0,0 +1,331 @@
//===-- Unittests for Comparison Operations for FPBits class -------------===//
Copy link
Member

Choose a reason for hiding this comment

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

Nit: formatting.

Suggested change
//===-- Unittests for Comparison Operations for FPBits class -------------===//
//===-- Unittests for Comparison Operations for FPBits class --------------===//

Copy link
Member

@overmighty overmighty left a comment

Choose a reason for hiding this comment

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

I had an extra review comment I thought I had sent but seemingly didn't.

Comment on lines 60 to 66
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan() ||
x_bits.is_quiet_nan() || y_bits.is_quiet_nan())
fputil::raise_except_if_required(FE_INVALID);

// Any comparison with NaN returns false
if (x_bits.is_nan() || y_bits.is_nan())
return false;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan() ||
x_bits.is_quiet_nan() || y_bits.is_quiet_nan())
fputil::raise_except_if_required(FE_INVALID);
// Any comparison with NaN returns false
if (x_bits.is_nan() || y_bits.is_nan())
return false;
if (x_bits.is_nan() || y_bits.is_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated, as of 4a69dc51

Comment on lines 50 to 52
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
auto test_qnan = [&](T x, T y) {
EXPECT_FALSE(equals(x, y));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
auto test_qnan = [&](T x, T y) {
EXPECT_FALSE(equals(x, y));
auto test_qnan = [&](T x, T y) {
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FALSE(equals(x, y));

Comment on lines 53 to 54
// NOTE: equals and not equals operations should raise FE_INVALID for
// quiet NaN operands
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// NOTE: equals and not equals operations should raise FE_INVALID for
// quiet NaN operands
// NOTE: equals and not equals operations should not raise FE_INVALID for
// quiet NaN operands

But I think what we should do instead is add comments at definition for fputil::equals, fputil::not_equals, etc, saying which IEEE 754 comparison predicate they implement. E.g., for fputil::equals:

// Implements the compareQuietEqual predicate as per IEEE Std 754-2019.
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> equals(T x,
                                                                       T y) {

Comment on lines 124 to 129
// NOTE: All signaling comparison operations other than equals and not
// equals should raise FE_INVALID when comparing with NaN
// see Note after table 5.1 in IEEE Std 754-2019
// [...] The Signaling predicates in Table 5.1 signal the invalid
// operation exception on quiet NaN operands to warn of potential
// incorrect behavior of programs written assuming trichotomy.
Copy link
Member

Choose a reason for hiding this comment

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

It's just all Signaling comparison operations, no exceptions. fputil::equals and fputil::not_equals implement Quiet predicates (compareQuietEqual, compareQuietNotEqual), but IEEE 754 also defines equivalent Signaling predicates (compareSignalingEqual, compareSignalingNotEqual).

Comment on lines 159 to 160
}
void test_greater_than() {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: spacing/formatting.

Suggested change
}
void test_greater_than() {
}
void test_greater_than() {

Comment on lines 332 to 339

// We have to use type alias here, since FPBits<(long double)> is not
// allowed if we do TEST_COMPARISON_OPS(LongDouble, (long double)); and
// Type(x) for Type = long double evaluates to long double(x)which is not
// allowed if we do TEST_COMPARISON_OPS(LongDouble, long double);
using long_double = long double;
TEST_COMPARISON_OPS(LongDouble, long_double)

Copy link
Member

Choose a reason for hiding this comment

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

This is no longer needed since the refactoring to use FEnvSafeTest.

Suggested change
// We have to use type alias here, since FPBits<(long double)> is not
// allowed if we do TEST_COMPARISON_OPS(LongDouble, (long double)); and
// Type(x) for Type = long double evaluates to long double(x)which is not
// allowed if we do TEST_COMPARISON_OPS(LongDouble, long double);
using long_double = long double;
TEST_COMPARISON_OPS(LongDouble, long_double)
TEST_COMPARISON_OPS(LongDouble, long double)

arsenm and others added 21 commits July 10, 2025 22:21
Emit a context error and delete the instruction. This
allows removing the AMDGPU hack where some atomic libcalls
are falsely added. NVPTX also later copied the same hack,
so remove it there too.

For now just emit the generic error, which is not good. It's
missing any useful context information (despite taking the instruction).
It's also confusing in the failed atomicrmw case, since it's reporting
failure at the intermediate failed cmpxchg instead of the original
atomicrmw.
…6829)

Before this patch, we emitted a bunch of irrelevant (to this test)
warnings:
```
  ../clang/test/SemaCXX/discrim-union.cpp:49:24: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
   49 |     constexpr const T &get(select<0>) { return val; }
      |                        ^
      |                                       const
../clang/test/SemaCXX/discrim-union.cpp:50:104: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
   50 |     template<unsigned N> constexpr const decltype(static_cast<const rest_t&>(rest).get(select<N-1>{})) get(select<N>) {
      |                                                                                                        ^
      |                                                                                                                       const
../clang/test/SemaCXX/discrim-union.cpp:82:22: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
   82 |   constexpr unsigned index() noexcept { return elem; }
      |                      ^
      |                              const
../clang/test/SemaCXX/discrim-union.cpp:88:33: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
   88 |   constexpr const_get_result<N> get() {
      |                                 ^
      |                                       const
../clang/test/SemaCXX/discrim-union.cpp:96:22: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Wconstexpr-not-const]
   96 |   constexpr const U &get() {
      |                      ^
      |                            const
5 warnings generated.
```
…ns (llvm#147570)

We were previously limited to abs(sub(zext(),zext()) patterns, but add
handling for a number of other abdu patterns until a topological sorted
dag allows us to rely on a ABDU node having already been created.

Now that we don't just match zext() sources, I've generalised the
createPSADBW helper to explicitly zext/truncate to the expected vXi8
source type - it still assumes the sources are correct for a PSADBW
node.

Fixes llvm#143456
.. produced by ADT/BitmaskEnum.h.

These aren't implicitly convertible to an integer, so I needed to tweak
a couple of bitwise operations and add an explicit constructor for
HexNumber and FlagEntry.

Motivation: I'd like to use this in the SFrame data structures (llvm#147264)
A disjoint OR can be transformed to an EOR.

We already lower EOR+NOT to EON, but not DisjointOR+NOT.
This generalizes the support added in llvm#99287 renaming the option to
RUNTIMES_USE_LIBC and integrating the module into libc++abi and
libunwind as well.
Instead, return the new Pointer. This was weird before because some
callers had to get the value from the stack again to perform further
operations.
…ple.c for AIX specific targets (llvm#147584)

This is a followup on the [[PowerPC][clang] Fix triple constructor
ambiguity causing "unknown" target triple on
AIX](llvm#147488 (comment)) to address
the [post-commit review
comment](llvm#147488 (comment)).

---------

Co-authored-by: Tony Varghese <[email protected]>
…ns (llvm#147245)

When collapsing linalg dimensions we check if its memref operands are
guaranteed to be collapsible. However, we currently assume that the
matching indexing map is the identity map.

This commit modifies this behavior and checks if the memref is
collapsible on the transformed dimensions.
…146082)

Previously we had a table of entries for every Libcall for
the comparison to use against an integer 0 if it was a soft
float compare function. This was only relevant to a handful of
opcodes, so it was wasteful. Now that we can distinguish the
abstract libcall for the compare with the concrete implementation,
we can just directly hardcode the comparison against the libcall
impl without this configuration system.
llvm#146083)

This fully consolidates all the calling convention configuration into
RuntimeLibcallInfo. I'm assuming that __aeabi functions have a universal
calling convention, and on other ABIs just don't use them. This will
enable splitting of RuntimeLibcallInfo into the ABI and lowering component.
… present (llvm#147089)

If the subtarget's latency is dependent on vl, then we shouldn't try to
fold away vsetvli toggles if it means increasing vl.
The code is correct as it aligns with the SPIR-V Specification, but the
comment was incorrect.
…same throughout the function (llvm#147506)

This change only applies to functions the can be reasonably expected to
use SVE registers.

Modifying vector length in the middle of a function might cause
incorrect stack deallocation if there are callee-saved SVE registers or
incorrect access to SVE stack slots.

Addresses (non-issue) llvm#143670
This commit removes the test file test-member-invalidation.cpp which was
recently introduced in 39bc052 by
splitting off a test case from new.cpp.

In that commit I preserved that test in a slightly modified setting to
demonstrate that it wasn't broken by the change; but in this separate
commit I'm removing it because I don't think that it "deserves a place"
among our tests.

The primary issue is that this test examines the values of data members
of a deleted object -- which is irrelevant, because code that relies on
the value of these members should be reported as a use-after-free bug.
(In fact, cplusplus.NewDelete reports it as a use-after-free bug, and
the checker family `MallocChecker` sinks the execution path even if that
particular frontend is not enabled.)

Moreover, a comment claimed that this tests "Invalidate Region even in
case of default destructor" while in fact it tested a situaton where the
destructor is a plain declared-but-not-defined method. The invalidation
of `this` is done by the conservative evaluation, and we don't need this
overcomplicated test to validate that very basic behavior.
boomanaiden154 and others added 16 commits July 10, 2025 22:29
This patch removes the buildkite_info object from
generate_test_report_lib as we no longer support buildkite so it is dead
code now. Eventually we should set up equivalent functionality on the
Github side for downloading the logs from a convenient link, but for now
clean up the code.

Reviewers: cmtice, DavidSpickett, lnihlen

Reviewed By: DavidSpickett

Pull Request: llvm#147784
This is another Buildkite relic that we can get rid of now to simplify
things a bit.

Reviewers: cmtice, DavidSpickett, lnihlen

Reviewed By: DavidSpickett

Pull Request: llvm#147791
This patch makes it so that generate_test_report_github.py generates a
test report even when we don't get any test results. This otherwise
created a pretty confusing user experience on the Github side if the
build failed before any tests ran or in cases like running check-libc
where none of the tests are run through lit.

Reviewers: lnihlen, cmtice

Pull Request: llvm#147871
…47951)

I'm surprised at how bad the test coverage is here. There is some
overlap with existing tests, but they aren't comprehensive and do
not cover all the ABIs, or all the different types.

Fixes llvm#147935
…std::nullopt_t) {}. (llvm#147816)

Our local Flang build on PowerPC was broken as
```
llvm/flang/../mlir/include/mlir/IR/ValueRange.h:401:20: error: 'ArrayRef' is deprecated: Use {} or ArrayRef<T>() instead [-Werror,-Wdeprecated-declarations]
  401 |       : ValueRange(ArrayRef<Value>(std::forward<Arg>(arg))) {}
      |                    ^
llvm/flang/lib/Optimizer/CodeGen/CodeGen.cpp:2243:53: note: in instantiation of function template specialization 'mlir::ValueRange::ValueRange<const std::nullopt_t &, void>' requested here
 2243 |                              /*cstInteriorIndices=*/std::nullopt, fieldIndices,
      |                                                     ^
 llvm/include/llvm/ADT/ArrayRef.h:70:18: note: 'ArrayRef' has been explicitly marked deprecated here
   70 |     /*implicit*/ LLVM_DEPRECATED("Use {} or ArrayRef<T>() instead", "{}")
      |                  ^
llvm/include/llvm/Support/Compiler.h:244:50: note: expanded from macro 'LLVM_DEPRECATED'
  244 | #define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX)))
      |                                                  ^
1 error generated.
```

This patch is to fix it.
…47943)

In the future, we want `ol_symbol_handle_t` to represent both kernels
and global variables The first step in this process is a rename and
promotion to a "typed handle".
…vectorizing operands (llvm#147583)"

This reverts commit ac4a38e.

This breaks the RVV builders
(MicroBenchmarks/ImageProcessing/Blur/blur.test and
MultiSource/Benchmarks/tramp3d-v4/tramp3d-v4.test from llvm-test-suite)
and reportedly SPEC Accel2023
<llvm#147583 (comment)>.
…ions (llvm#147927)

This is a fix for llvm#136102. It
missed scoping for `DeclareFuncOps`.
In scenarios with multiple function declarations, the `valueMapper`
wasn't updated and later uses of values in other functions still used
the assigned names in prior functions.

This is visible in the reproducer here
iree-org/iree#21303: Although the counter for
variable enumeration was reset, as it is visible for the local vars, the
function arguments were mapped to old names. Due to this mapping, the
counter was never increased, and the local variables conflicted with the
arguments.

This fix adds proper scoping for declarations and a test-case to cover
the scenario with multiple `DeclareFuncOps`.
A experimental_vp_reverse isn't exactly functionally the same as
vector_reverse, so previously it wasn't getting picked up by the generic
VP costing code that reuses the non-VP equivalents.

But for costing purposes it's good enough so we can reuse it.

The type-based cost for vector_reverse is still incorrect and should be
fixed in another patch.
This PR adds overrides in `SPIRVTTIImpl` for
`collectFlatAddressOperands` and `rewriteIntrinsicWithAddressSpace` to
enable `InferAddressSpacesPass` to rewrite the
`llvm.spv.generic.cast.to.ptr.explicit` intrinsic (corresponding to
`OpGenericCastToPtrExplicit`) when the address space of the argument can
be inferred. When the destination address space of the cast matches the
inferred address space of the argument, the call is replaced with that
argument. When they do not match, the cast is replaced with a constant
null pointer.
The `-mframe-pointer` flag is not explicitly set in the original `flang`
invocation and so the value passed to `flang -fc1` can vary depending on
the host machine, so don't verify it in the output.

`-mframe-pointer` forwarding is already verified by
`flang/test/Driver/frame-pointer-forwarding.f90`.
Summary:
Recent changes altered the name without updating this, add it in and
also tell the builtins build that C++ compilers work because it seems to
require that now.
When the RegisterCoalescer adds an implicit-def when coalescing
a SUBREG_TO_REG (llvm#123632), this causes issues when removing other
COPY nodes by commuting the instruction because it doesn't take
the implicit-def into consideration. This PR fixes that.
We should not include both linux/prctl.h and sys/prctl.h. This works
with glibc because the latter includes the former, but breaks with musl
because the latter redeclares the contents of the former, resulting in:

```
/usr/local/aarch64-linux-musl/include/sys/prctl.h:88:8: error:
redefinition of 'struct prctl_mm_map'
       88 | struct prctl_mm_map {
          |        ^~~~~~~~~~~~
In file included from
/checkout/src/llvm-project/llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp:13:
/usr/local/aarch64-linux-musl/include/linux/prctl.h:134:8: note:
previous definition of 'struct prctl_mm_map'
      134 | struct prctl_mm_map {
          |        ^~~~~~~~~~~~
```

Fixes llvm#139443.
Signed-off-by: Krishna Pandey <[email protected]>
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.

None yet