[clang] [Clang] `constexpr` builtin floating point classification / comparison functions (PR #94118)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 1 11:47:49 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Mital Ashok (MitalAshok)

<details>
<summary>Changes</summary>

As per [P0533R9](https://wg21.link/P0533R9), the corresponding C++ `[c.math.fpclass]` standard library functions for the C macros are now `constexpr`.

The only classification function that wasn't already `constexpr` was `__builtin_signbit`.
The floating point comparison functions `__builtin_isgreater`, `__builtin_isgreaterequal`, `__builtin_isless`, `__builtin_islessequal`, `__builtin_islessgreater` and `__builtin_isunordered` are now `constexpr`.
The C23 macro `iseqsig` is not currently supported because `__bulitin_iseqsig` doesn't exist yet (and C++26 is still currently based on C18).

This also allows them to be constant folded in C, matching the behaviour of GCC.


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


4 Files Affected:

- (modified) clang/include/clang/Basic/Builtins.td (+11-9) 
- (modified) clang/lib/AST/ExprConstant.cpp (+67) 
- (modified) clang/lib/AST/Interp/InterpBuiltin.cpp (+87) 
- (modified) clang/test/Sema/constant-builtins-2.c (+48) 


``````````diff
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 11982af3fa609..7b335e43f8c0e 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -533,42 +533,42 @@ def BuiltinComplex : Builtin {
 def IsGreater : Builtin {
   let Spellings = ["__builtin_isgreater"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def IsGreaterEqual : Builtin {
   let Spellings = ["__builtin_isgreaterequal"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def IsLess : Builtin {
   let Spellings = ["__builtin_isless"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def IsLessEqual : Builtin {
   let Spellings = ["__builtin_islessequal"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def IsLessGreater : Builtin {
   let Spellings = ["__builtin_islessgreater"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def IsUnordered : Builtin {
   let Spellings = ["__builtin_isunordered"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
@@ -646,19 +646,21 @@ def IsFPClass : Builtin {
 def Signbit : Builtin {
   let Spellings = ["__builtin_signbit"];
   let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
-                    CustomTypeChecking];
+                    CustomTypeChecking, Constexpr];
   let Prototype = "int(...)";
 }
 
 def SignbitF : Builtin {
   let Spellings = ["__builtin_signbitf"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
+                    Constexpr];
   let Prototype = "int(float)";
 }
 
 def SignbitL : Builtin {
   let Spellings = ["__builtin_signbitl"];
-  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
+  let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
+                    Constexpr];
   let Prototype = "int(long double)";
 }
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f1aa19e4409e1..cf715857f1370 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12650,6 +12650,73 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
            Success(Val.isZero() ? 1 : 0, E);
   }
 
+  case Builtin::BI__builtin_signbit:
+  case Builtin::BI__builtin_signbitf:
+  case Builtin::BI__builtin_signbitl: {
+    APFloat Val(0.0);
+    return EvaluateFloat(E->getArg(0), Val, Info) &&
+           Success(Val.isNegative() ? 1 : 0, E);
+  }
+
+  case Builtin::BI__builtin_isgreater:
+  case Builtin::BI__builtin_isgreaterequal:
+  case Builtin::BI__builtin_isless:
+  case Builtin::BI__builtin_islessequal:
+  case Builtin::BI__builtin_islessgreater:
+  case Builtin::BI__builtin_isunordered: {
+    APFloat LHS(0.0);
+    APFloat RHS(0.0);
+    if (!EvaluateFloat(E->getArg(0), LHS, Info) ||
+        !EvaluateFloat(E->getArg(1), RHS, Info))
+      return false;
+
+    APFloat::cmpResult Cmp = LHS.compare(RHS);
+    bool FunctionResult;
+    if (BuiltinOp == Builtin::BI__builtin_isunordered ||
+        Cmp == APFloat::cmpResult::cmpUnordered) {
+      FunctionResult = BuiltinOp == Builtin::BI__builtin_isunordered &&
+                       Cmp == APFloat::cmpResult::cmpUnordered;
+    } else {
+      int CmpStrong;
+      switch (Cmp) {
+      case APFloat::cmpResult::cmpEqual:
+        CmpStrong = 0;
+        break;
+      case APFloat::cmpResult::cmpLessThan:
+        CmpStrong = -1;
+        break;
+      case APFloat::cmpResult::cmpGreaterThan:
+        CmpStrong = 1;
+        break;
+      default:
+        llvm_unreachable("Unchecked cmpResult enum");
+      }
+
+      switch (BuiltinOp) {
+      case Builtin::BI__builtin_isgreater:
+        FunctionResult = CmpStrong > 0;
+        break;
+      case Builtin::BI__builtin_isgreaterequal:
+        FunctionResult = CmpStrong >= 0;
+        break;
+      case Builtin::BI__builtin_isless:
+        FunctionResult = CmpStrong < 0;
+        break;
+      case Builtin::BI__builtin_islessequal:
+        FunctionResult = CmpStrong <= 0;
+        break;
+      case Builtin::BI__builtin_islessgreater:
+        FunctionResult = CmpStrong != 0;
+        break;
+      default:
+        llvm_unreachable("Unexpected builtin ID: Should be a floating point "
+                         "comparison function");
+      }
+    }
+
+    return Success(FunctionResult ? 1 : 0, E);
+  }
+
   case Builtin::BI__builtin_issignaling: {
     APFloat Val(0.0);
     return EvaluateFloat(E->getArg(0), Val, Info) &&
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 00206d09c113d..69e2960269c48 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -430,6 +430,78 @@ static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_signbit(InterpState &S, CodePtr OpPC,
+                                    const InterpFrame *Frame, const Function *F,
+                                    const CallExpr *Call) {
+  const Floating &Arg = S.Stk.peek<Floating>();
+
+  pushInteger(S, Arg.isNegative(), Call->getType());
+  return true;
+}
+
+static bool interp_floating_comparison(InterpState &S, CodePtr OpPC,
+                                       const InterpFrame *Frame,
+                                       const Function *F,
+                                       const CallExpr *Call) {
+  const Floating &RHS = S.Stk.peek<Floating>();
+  const Floating &LHS = S.Stk.peek<Floating>(align(2u * primSize(PT_Float)));
+  unsigned ID = F->getBuiltinID();
+  assert(ID == Builtin::BI__builtin_isgreater ||
+         ID == Builtin::BI__builtin_isgreaterequal ||
+         ID == Builtin::BI__builtin_isless ||
+         ID == Builtin::BI__builtin_islessequal ||
+         ID == Builtin::BI__builtin_islessgreater ||
+         ID == Builtin::BI__builtin_isunordered);
+
+  ComparisonCategoryResult Cmp = LHS.compare(RHS);
+  bool FunctionResult;
+  if (ID == Builtin::BI__builtin_isunordered ||
+      Cmp == ComparisonCategoryResult::Unordered) {
+    FunctionResult = ID == Builtin::BI__builtin_isunordered &&
+                     Cmp == ComparisonCategoryResult::Unordered;
+  } else {
+    int CmpStrong;
+    switch (Cmp) {
+    case ComparisonCategoryResult::Equal:
+    case ComparisonCategoryResult::Equivalent:
+      CmpStrong = 0;
+      break;
+    case ComparisonCategoryResult::Less:
+      CmpStrong = -1;
+      break;
+    case ComparisonCategoryResult::Greater:
+      CmpStrong = 1;
+      break;
+    default:
+      llvm_unreachable("Unchecked ComparisonCategoryResult enum");
+    }
+
+    switch (ID) {
+    case Builtin::BI__builtin_isgreater:
+      FunctionResult = CmpStrong > 0;
+      break;
+    case Builtin::BI__builtin_isgreaterequal:
+      FunctionResult = CmpStrong >= 0;
+      break;
+    case Builtin::BI__builtin_isless:
+      FunctionResult = CmpStrong < 0;
+      break;
+    case Builtin::BI__builtin_islessequal:
+      FunctionResult = CmpStrong <= 0;
+      break;
+    case Builtin::BI__builtin_islessgreater:
+      FunctionResult = CmpStrong != 0;
+      break;
+    default:
+      llvm_unreachable("Unexpected builtin ID: Should be a floating point "
+                       "comparison function");
+    }
+  }
+
+  pushInteger(S, FunctionResult, Call->getType());
+  return true;
+}
+
 /// First parameter to __builtin_isfpclass is the floating value, the
 /// second one is an integral value.
 static bool interp__builtin_isfpclass(InterpState &S, CodePtr OpPC,
@@ -1214,6 +1286,21 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
     if (!interp__builtin_iszero(S, OpPC, Frame, F, Call))
       return false;
     break;
+  case Builtin::BI__builtin_signbit:
+  case Builtin::BI__builtin_signbitf:
+  case Builtin::BI__builtin_signbitl:
+    if (!interp__builtin_signbit(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+  case Builtin::BI__builtin_isgreater:
+  case Builtin::BI__builtin_isgreaterequal:
+  case Builtin::BI__builtin_isless:
+  case Builtin::BI__builtin_islessequal:
+  case Builtin::BI__builtin_islessgreater:
+  case Builtin::BI__builtin_isunordered:
+    if (!interp_floating_comparison(S, OpPC, Frame, F, Call))
+      return false;
+    break;
   case Builtin::BI__builtin_isfpclass:
     if (!interp__builtin_isfpclass(S, OpPC, Frame, F, Call))
       return false;
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index a60a1f16a4587..59afdf056ae9f 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fexperimental-new-constant-interpreter -verify %s
 
 // Math stuff
 
@@ -204,6 +205,53 @@ char isfpclass_snan_1   [!__builtin_isfpclass(__builtin_nans(""), 0x0002) ? 1 :
 char isfpclass_snan_2   [__builtin_isfpclass(__builtin_nansl(""), 0x0207) ? 1 : -1]; // ~fcFinite
 char isfpclass_snan_3   [!__builtin_isfpclass(__builtin_nans(""), 0x01F8) ? 1 : -1]; // fcFinite
 
+__extension__ _Static_assert(
+  !__builtin_signbit(1.0) && __builtin_signbit(-1.0) && !__builtin_signbit(0.0) && __builtin_signbit(-0.0) &&
+  !__builtin_signbitf(1.0f) && __builtin_signbitf(-1.0f) && !__builtin_signbitf(0.0f) && __builtin_signbitf(-0.0f) &&
+  !__builtin_signbitl(1.0L) && __builtin_signbitf(-1.0L) && !__builtin_signbitf(0.0L) && __builtin_signbitf(-0.0L) &&
+  !__builtin_signbit(1.0f) && __builtin_signbit(-1.0f) && !__builtin_signbit(0.0f) && __builtin_signbit(-0.0f) &&
+  !__builtin_signbit(1.0L) && __builtin_signbit(-1.0L) && !__builtin_signbit(0.0L) && __builtin_signbit(-0.0L) &&
+#if defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)
+  !__builtin_signbit(1.0q) && __builtin_signbit(-1.0q) && !__builtin_signbit(0.0q) && __builtin_signbit(-0.0q) &&
+#endif
+  1, ""
+);
+
+#define LESS(X, Y) \
+  !__builtin_isgreater(X, Y) && __builtin_isgreater(Y, X) &&             \
+  !__builtin_isgreaterequal(X, Y) && __builtin_isgreaterequal(Y, X) &&   \
+  __builtin_isless(X, Y) && !__builtin_isless(Y, X) &&                   \
+  __builtin_islessequal(X, Y) && !__builtin_islessequal(Y, X) &&         \
+  __builtin_islessgreater(X, Y) && __builtin_islessgreater(Y, X) &&      \
+  !__builtin_isunordered(X, Y) && !__builtin_isunordered(Y, X)
+#define EQUAL(X, Y) \
+  !__builtin_isgreater(X, Y) && !__builtin_isgreater(Y, X) &&            \
+  __builtin_isgreaterequal(X, Y) && __builtin_isgreaterequal(Y, X) &&    \
+  !__builtin_isless(X, Y) && !__builtin_isless(Y, X) &&                  \
+  __builtin_islessequal(X, Y) && __builtin_islessequal(Y, X) &&          \
+  !__builtin_islessgreater(X, Y) && !__builtin_islessgreater(Y, X) &&    \
+  !__builtin_isunordered(X, Y) && !__builtin_isunordered(Y, X)
+#define UNORDERED(X, Y) \
+  !__builtin_isgreater(X, Y) && !__builtin_isgreater(Y, X) &&            \
+  !__builtin_isgreaterequal(X, Y) && !__builtin_isgreaterequal(Y, X) &&  \
+  !__builtin_isless(X, Y) && !__builtin_isless(Y, X) &&                  \
+  !__builtin_islessequal(X, Y) && !__builtin_islessequal(Y, X) &&        \
+  !__builtin_islessgreater(X, Y) && !__builtin_islessgreater(Y, X) &&    \
+  __builtin_isunordered(X, Y) && __builtin_isunordered(Y, X)
+
+__extension__ _Static_assert(
+  LESS(0.0, 1.0) && EQUAL(1.0, 1.0) && EQUAL(0.0, -0.0) &&
+  UNORDERED(__builtin_nan(""), 1.0) && UNORDERED(__builtin_nan(""), __builtin_inf()) && LESS(0.0, __builtin_inf()) &&
+  LESS(0.0f, 1.0f) && EQUAL(1.0f, 1.0f) && EQUAL(0.0f, -0.0f) &&
+  UNORDERED(__builtin_nanf(""), 1.0f) && UNORDERED(__builtin_nanf(""), __builtin_inff()) && LESS(0.0f, __builtin_inff()) &&
+  LESS(0.0L, 1.0L) && EQUAL(1.0L, 1.0L) && EQUAL(0.0L, -0.0L) &&
+  UNORDERED(__builtin_nanl(""), 1.0L) && UNORDERED(__builtin_nanl(""), __builtin_infl()) && LESS(0.0L, __builtin_infl()) &&
+#if defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)
+  LESS(0.0q, 1.0q) && EQUAL(1.0q, 1.0q) && EQUAL(0.0q, -0.0q) &&
+#endif
+  1, ""
+);
+
 //double       g19 = __builtin_powi(2.0, 4);
 //float        g20 = __builtin_powif(2.0f, 4);
 //long double  g21 = __builtin_powil(2.0L, 4);

``````````

</details>


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


More information about the cfe-commits mailing list