[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