https://github.com/MitalAshok created https://github.com/llvm/llvm-project/pull/94118
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. >From ed1c00ee4474a626965290f2d16aaaf0f4519ec9 Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Sat, 1 Jun 2024 17:45:21 +0100 Subject: [PATCH 1/2] constexpr __builtin_signbit --- clang/include/clang/Basic/Builtins.td | 8 +++++--- clang/lib/AST/ExprConstant.cpp | 8 ++++++++ clang/lib/AST/Interp/InterpBuiltin.cpp | 15 +++++++++++++++ clang/test/Sema/constant-builtins-2.c | 13 +++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 11982af3fa609..f784711bc04dc 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -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..b4de743c4d95b 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12650,6 +12650,14 @@ 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_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..4ca92e66b2912 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -430,6 +430,15 @@ 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; +} + /// 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 +1223,12 @@ 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_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..fca4ac2a26898 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,18 @@ 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, "" +); + //double g19 = __builtin_powi(2.0, 4); //float g20 = __builtin_powif(2.0f, 4); //long double g21 = __builtin_powil(2.0L, 4); >From f08f0c4e6866fba98507a6ba7e817f586b92994f Mon Sep 17 00:00:00 2001 From: Mital Ashok <mi...@mitalashok.co.uk> Date: Sat, 1 Jun 2024 19:24:24 +0100 Subject: [PATCH 2/2] constexpr __builtin_is{less|greater|...|unordered} --- clang/include/clang/Basic/Builtins.td | 12 ++--- clang/lib/AST/ExprConstant.cpp | 59 +++++++++++++++++++++ clang/lib/AST/Interp/InterpBuiltin.cpp | 72 ++++++++++++++++++++++++++ clang/test/Sema/constant-builtins-2.c | 35 +++++++++++++ 4 files changed, 172 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index f784711bc04dc..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(...)"; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index b4de743c4d95b..cf715857f1370 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12658,6 +12658,65 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, 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 4ca92e66b2912..69e2960269c48 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -439,6 +439,69 @@ static bool interp__builtin_signbit(InterpState &S, CodePtr OpPC, 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, @@ -1229,6 +1292,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, 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 fca4ac2a26898..59afdf056ae9f 100644 --- a/clang/test/Sema/constant-builtins-2.c +++ b/clang/test/Sema/constant-builtins-2.c @@ -217,6 +217,41 @@ __extension__ _Static_assert( 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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits