[Clang] constexpr builtin floating point classification / comparison functions by MitalAshok · Pull Request #94118 · llvm/llvm-project (original) (raw)
@llvm/pr-subscribers-clang
Author: Mital Ashok (MitalAshok)
Changes
As per 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 --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];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def IsGreaterEqual : Builtin { let Spellings = ["__builtin_isgreaterequal"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def IsLess : Builtin { let Spellings = ["__builtin_isless"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def IsLessEqual : Builtin { let Spellings = ["__builtin_islessequal"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def IsLessGreater : Builtin { let Spellings = ["__builtin_islessgreater"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def IsUnordered : Builtin { let Spellings = ["__builtin_isunordered"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
@@ -646,19 +646,21 @@ def IsFPClass : Builtin { def Signbit : Builtin { let Spellings = ["__builtin_signbit"]; let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
let Prototype = "int(...)"; }CustomTypeChecking, Constexpr];
def SignbitF : Builtin { let Spellings = ["__builtin_signbitf"];
- let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
- let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
let Prototype = "int(float)"; }Constexpr];
def SignbitL : Builtin { let Spellings = ["__builtin_signbitl"];
- let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
- let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
let Prototype = "int(long double)"; }Constexpr];
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();
- 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();
- const Floating &LHS = S.Stk.peek(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);