[clang] [Clang] `constexpr` builtin floating point classification / comparison functions (PR #94118)
Mital Ashok via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 18 01:34:41 PDT 2024
https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/94118
>From ed1c00ee4474a626965290f2d16aaaf0f4519ec9 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sat, 1 Jun 2024 17:45:21 +0100
Subject: [PATCH 1/5] 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 11982af3fa609b..f784711bc04dc1 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 f1aa19e4409e15..b4de743c4d95bf 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 00206d09c113d9..4ca92e66b29129 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 a60a1f16a45874..fca4ac2a26898f 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 <mital at mitalashok.co.uk>
Date: Sat, 1 Jun 2024 19:24:24 +0100
Subject: [PATCH 2/5] 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 f784711bc04dc1..7b335e43f8c0ee 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 b4de743c4d95bf..cf715857f13707 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 4ca92e66b29129..69e2960269c480 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 fca4ac2a26898f..59afdf056ae9ff 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);
>From 0d9de42fd70f34a42bc859932c24ff5b1627a26c Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sat, 1 Jun 2024 20:15:43 +0100
Subject: [PATCH 3/5] Regenerate clang/test/Analysis/builtin_signbit.cpp
Three of those lines are constant folded now
---
clang/test/Analysis/builtin_signbit.cpp | 126 ++++++++++--------------
1 file changed, 53 insertions(+), 73 deletions(-)
diff --git a/clang/test/Analysis/builtin_signbit.cpp b/clang/test/Analysis/builtin_signbit.cpp
index be10f0950f69b7..ad185d855cfff3 100644
--- a/clang/test/Analysis/builtin_signbit.cpp
+++ b/clang/test/Analysis/builtin_signbit.cpp
@@ -12,103 +12,83 @@ long double ld = -1.0L;
// CHECK-BE32-LABEL: define dso_local void @_Z12test_signbitv(
// CHECK-BE32-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-BE32-NEXT: entry:
-// CHECK-BE32-NEXT: [[TMP0:%.*]] = lshr i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128), 64
-// CHECK-BE32-NEXT: [[TMP1:%.*]] = trunc i128 [[TMP0]] to i64
-// CHECK-BE32-NEXT: [[TMP2:%.*]] = icmp slt i64 [[TMP1]], 0
-// CHECK-BE32-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP2]] to i8
+// CHECK-BE32-NEXT: store i8 0, ptr @b, align 1
+// CHECK-BE32-NEXT: [[TMP0:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-BE32-NEXT: [[TMP1:%.*]] = bitcast ppc_fp128 [[TMP0]] to i128
+// CHECK-BE32-NEXT: [[TMP2:%.*]] = lshr i128 [[TMP1]], 64
+// CHECK-BE32-NEXT: [[TMP3:%.*]] = trunc i128 [[TMP2]] to i64
+// CHECK-BE32-NEXT: [[TMP4:%.*]] = icmp slt i64 [[TMP3]], 0
+// CHECK-BE32-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP4]] to i8
// CHECK-BE32-NEXT: store i8 [[FROMBOOL]], ptr @b, align 1
-// CHECK-BE32-NEXT: [[TMP3:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-BE32-NEXT: [[TMP4:%.*]] = bitcast ppc_fp128 [[TMP3]] to i128
-// CHECK-BE32-NEXT: [[TMP5:%.*]] = lshr i128 [[TMP4]], 64
-// CHECK-BE32-NEXT: [[TMP6:%.*]] = trunc i128 [[TMP5]] to i64
-// CHECK-BE32-NEXT: [[TMP7:%.*]] = icmp slt i64 [[TMP6]], 0
+// CHECK-BE32-NEXT: store i8 0, ptr @b, align 1
+// CHECK-BE32-NEXT: [[TMP5:%.*]] = load double, ptr @d, align 8
+// CHECK-BE32-NEXT: [[CONV:%.*]] = fptrunc double [[TMP5]] to float
+// CHECK-BE32-NEXT: [[TMP6:%.*]] = bitcast float [[CONV]] to i32
+// CHECK-BE32-NEXT: [[TMP7:%.*]] = icmp slt i32 [[TMP6]], 0
// CHECK-BE32-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[TMP7]] to i8
// CHECK-BE32-NEXT: store i8 [[FROMBOOL1]], ptr @b, align 1
// CHECK-BE32-NEXT: store i8 0, ptr @b, align 1
-// CHECK-BE32-NEXT: [[TMP8:%.*]] = load double, ptr @d, align 8
-// CHECK-BE32-NEXT: [[CONV:%.*]] = fptrunc double [[TMP8]] to float
-// CHECK-BE32-NEXT: [[TMP9:%.*]] = bitcast float [[CONV]] to i32
-// CHECK-BE32-NEXT: [[TMP10:%.*]] = icmp slt i32 [[TMP9]], 0
-// CHECK-BE32-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP10]] to i8
+// CHECK-BE32-NEXT: [[TMP8:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-BE32-NEXT: [[TMP9:%.*]] = bitcast ppc_fp128 [[TMP8]] to i128
+// CHECK-BE32-NEXT: [[TMP10:%.*]] = lshr i128 [[TMP9]], 64
+// CHECK-BE32-NEXT: [[TMP11:%.*]] = trunc i128 [[TMP10]] to i64
+// CHECK-BE32-NEXT: [[TMP12:%.*]] = icmp slt i64 [[TMP11]], 0
+// CHECK-BE32-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP12]] to i8
// CHECK-BE32-NEXT: store i8 [[FROMBOOL2]], ptr @b, align 1
-// CHECK-BE32-NEXT: [[TMP11:%.*]] = lshr i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128), 64
-// CHECK-BE32-NEXT: [[TMP12:%.*]] = trunc i128 [[TMP11]] to i64
-// CHECK-BE32-NEXT: [[TMP13:%.*]] = icmp slt i64 [[TMP12]], 0
-// CHECK-BE32-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[TMP13]] to i8
-// CHECK-BE32-NEXT: store i8 [[FROMBOOL3]], ptr @b, align 1
-// CHECK-BE32-NEXT: [[TMP14:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-BE32-NEXT: [[TMP15:%.*]] = bitcast ppc_fp128 [[TMP14]] to i128
-// CHECK-BE32-NEXT: [[TMP16:%.*]] = lshr i128 [[TMP15]], 64
-// CHECK-BE32-NEXT: [[TMP17:%.*]] = trunc i128 [[TMP16]] to i64
-// CHECK-BE32-NEXT: [[TMP18:%.*]] = icmp slt i64 [[TMP17]], 0
-// CHECK-BE32-NEXT: [[FROMBOOL4:%.*]] = zext i1 [[TMP18]] to i8
-// CHECK-BE32-NEXT: store i8 [[FROMBOOL4]], ptr @b, align 1
// CHECK-BE32-NEXT: ret void
//
// CHECK-BE64-LABEL: define dso_local void @_Z12test_signbitv(
// CHECK-BE64-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-BE64-NEXT: entry:
-// CHECK-BE64-NEXT: [[TMP0:%.*]] = lshr i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128), 64
-// CHECK-BE64-NEXT: [[TMP1:%.*]] = trunc i128 [[TMP0]] to i64
-// CHECK-BE64-NEXT: [[TMP2:%.*]] = icmp slt i64 [[TMP1]], 0
-// CHECK-BE64-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP2]] to i8
+// CHECK-BE64-NEXT: store i8 0, ptr @b, align 1
+// CHECK-BE64-NEXT: [[TMP0:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-BE64-NEXT: [[TMP1:%.*]] = bitcast ppc_fp128 [[TMP0]] to i128
+// CHECK-BE64-NEXT: [[TMP2:%.*]] = lshr i128 [[TMP1]], 64
+// CHECK-BE64-NEXT: [[TMP3:%.*]] = trunc i128 [[TMP2]] to i64
+// CHECK-BE64-NEXT: [[TMP4:%.*]] = icmp slt i64 [[TMP3]], 0
+// CHECK-BE64-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP4]] to i8
// CHECK-BE64-NEXT: store i8 [[FROMBOOL]], ptr @b, align 1
-// CHECK-BE64-NEXT: [[TMP3:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-BE64-NEXT: [[TMP4:%.*]] = bitcast ppc_fp128 [[TMP3]] to i128
-// CHECK-BE64-NEXT: [[TMP5:%.*]] = lshr i128 [[TMP4]], 64
-// CHECK-BE64-NEXT: [[TMP6:%.*]] = trunc i128 [[TMP5]] to i64
-// CHECK-BE64-NEXT: [[TMP7:%.*]] = icmp slt i64 [[TMP6]], 0
+// CHECK-BE64-NEXT: store i8 0, ptr @b, align 1
+// CHECK-BE64-NEXT: [[TMP5:%.*]] = load double, ptr @d, align 8
+// CHECK-BE64-NEXT: [[CONV:%.*]] = fptrunc double [[TMP5]] to float
+// CHECK-BE64-NEXT: [[TMP6:%.*]] = bitcast float [[CONV]] to i32
+// CHECK-BE64-NEXT: [[TMP7:%.*]] = icmp slt i32 [[TMP6]], 0
// CHECK-BE64-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[TMP7]] to i8
// CHECK-BE64-NEXT: store i8 [[FROMBOOL1]], ptr @b, align 1
// CHECK-BE64-NEXT: store i8 0, ptr @b, align 1
-// CHECK-BE64-NEXT: [[TMP8:%.*]] = load double, ptr @d, align 8
-// CHECK-BE64-NEXT: [[CONV:%.*]] = fptrunc double [[TMP8]] to float
-// CHECK-BE64-NEXT: [[TMP9:%.*]] = bitcast float [[CONV]] to i32
-// CHECK-BE64-NEXT: [[TMP10:%.*]] = icmp slt i32 [[TMP9]], 0
-// CHECK-BE64-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP10]] to i8
+// CHECK-BE64-NEXT: [[TMP8:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-BE64-NEXT: [[TMP9:%.*]] = bitcast ppc_fp128 [[TMP8]] to i128
+// CHECK-BE64-NEXT: [[TMP10:%.*]] = lshr i128 [[TMP9]], 64
+// CHECK-BE64-NEXT: [[TMP11:%.*]] = trunc i128 [[TMP10]] to i64
+// CHECK-BE64-NEXT: [[TMP12:%.*]] = icmp slt i64 [[TMP11]], 0
+// CHECK-BE64-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP12]] to i8
// CHECK-BE64-NEXT: store i8 [[FROMBOOL2]], ptr @b, align 1
-// CHECK-BE64-NEXT: [[TMP11:%.*]] = lshr i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128), 64
-// CHECK-BE64-NEXT: [[TMP12:%.*]] = trunc i128 [[TMP11]] to i64
-// CHECK-BE64-NEXT: [[TMP13:%.*]] = icmp slt i64 [[TMP12]], 0
-// CHECK-BE64-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[TMP13]] to i8
-// CHECK-BE64-NEXT: store i8 [[FROMBOOL3]], ptr @b, align 1
-// CHECK-BE64-NEXT: [[TMP14:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-BE64-NEXT: [[TMP15:%.*]] = bitcast ppc_fp128 [[TMP14]] to i128
-// CHECK-BE64-NEXT: [[TMP16:%.*]] = lshr i128 [[TMP15]], 64
-// CHECK-BE64-NEXT: [[TMP17:%.*]] = trunc i128 [[TMP16]] to i64
-// CHECK-BE64-NEXT: [[TMP18:%.*]] = icmp slt i64 [[TMP17]], 0
-// CHECK-BE64-NEXT: [[FROMBOOL4:%.*]] = zext i1 [[TMP18]] to i8
-// CHECK-BE64-NEXT: store i8 [[FROMBOOL4]], ptr @b, align 1
// CHECK-BE64-NEXT: ret void
//
// CHECK-LE-LABEL: define dso_local void @_Z12test_signbitv(
// CHECK-LE-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-LE-NEXT: entry:
-// CHECK-LE-NEXT: [[TMP0:%.*]] = icmp slt i64 trunc (i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128) to i64), 0
-// CHECK-LE-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP0]] to i8
+// CHECK-LE-NEXT: store i8 0, ptr @b, align 1
+// CHECK-LE-NEXT: [[TMP0:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-LE-NEXT: [[TMP1:%.*]] = bitcast ppc_fp128 [[TMP0]] to i128
+// CHECK-LE-NEXT: [[TMP2:%.*]] = trunc i128 [[TMP1]] to i64
+// CHECK-LE-NEXT: [[TMP3:%.*]] = icmp slt i64 [[TMP2]], 0
+// CHECK-LE-NEXT: [[FROMBOOL:%.*]] = zext i1 [[TMP3]] to i8
// CHECK-LE-NEXT: store i8 [[FROMBOOL]], ptr @b, align 1
-// CHECK-LE-NEXT: [[TMP1:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-LE-NEXT: [[TMP2:%.*]] = bitcast ppc_fp128 [[TMP1]] to i128
-// CHECK-LE-NEXT: [[TMP3:%.*]] = trunc i128 [[TMP2]] to i64
-// CHECK-LE-NEXT: [[TMP4:%.*]] = icmp slt i64 [[TMP3]], 0
-// CHECK-LE-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[TMP4]] to i8
+// CHECK-LE-NEXT: store i8 0, ptr @b, align 1
+// CHECK-LE-NEXT: [[TMP4:%.*]] = load double, ptr @d, align 8
+// CHECK-LE-NEXT: [[CONV:%.*]] = fptrunc double [[TMP4]] to float
+// CHECK-LE-NEXT: [[TMP5:%.*]] = bitcast float [[CONV]] to i32
+// CHECK-LE-NEXT: [[TMP6:%.*]] = icmp slt i32 [[TMP5]], 0
+// CHECK-LE-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[TMP6]] to i8
// CHECK-LE-NEXT: store i8 [[FROMBOOL1]], ptr @b, align 1
// CHECK-LE-NEXT: store i8 0, ptr @b, align 1
-// CHECK-LE-NEXT: [[TMP5:%.*]] = load double, ptr @d, align 8
-// CHECK-LE-NEXT: [[CONV:%.*]] = fptrunc double [[TMP5]] to float
-// CHECK-LE-NEXT: [[TMP6:%.*]] = bitcast float [[CONV]] to i32
-// CHECK-LE-NEXT: [[TMP7:%.*]] = icmp slt i32 [[TMP6]], 0
-// CHECK-LE-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP7]] to i8
+// CHECK-LE-NEXT: [[TMP7:%.*]] = load ppc_fp128, ptr @ld, align 16
+// CHECK-LE-NEXT: [[TMP8:%.*]] = bitcast ppc_fp128 [[TMP7]] to i128
+// CHECK-LE-NEXT: [[TMP9:%.*]] = trunc i128 [[TMP8]] to i64
+// CHECK-LE-NEXT: [[TMP10:%.*]] = icmp slt i64 [[TMP9]], 0
+// CHECK-LE-NEXT: [[FROMBOOL2:%.*]] = zext i1 [[TMP10]] to i8
// CHECK-LE-NEXT: store i8 [[FROMBOOL2]], ptr @b, align 1
-// CHECK-LE-NEXT: [[TMP8:%.*]] = icmp slt i64 trunc (i128 bitcast (ppc_fp128 0xM3FF00000000000000000000000000000 to i128) to i64), 0
-// CHECK-LE-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[TMP8]] to i8
-// CHECK-LE-NEXT: store i8 [[FROMBOOL3]], ptr @b, align 1
-// CHECK-LE-NEXT: [[TMP9:%.*]] = load ppc_fp128, ptr @ld, align 16
-// CHECK-LE-NEXT: [[TMP10:%.*]] = bitcast ppc_fp128 [[TMP9]] to i128
-// CHECK-LE-NEXT: [[TMP11:%.*]] = trunc i128 [[TMP10]] to i64
-// CHECK-LE-NEXT: [[TMP12:%.*]] = icmp slt i64 [[TMP11]], 0
-// CHECK-LE-NEXT: [[FROMBOOL4:%.*]] = zext i1 [[TMP12]] to i8
-// CHECK-LE-NEXT: store i8 [[FROMBOOL4]], ptr @b, align 1
// CHECK-LE-NEXT: ret void
//
void test_signbit()
>From 49e9468723c74a4ddb44c6b5ed94d1223e670036 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Tue, 4 Jun 2024 18:16:47 +0100
Subject: [PATCH 4/5] Add release note
---
clang/docs/ReleaseNotes.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0c700d23257bf7..7fbd10b3180476 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -323,6 +323,10 @@ Non-comprehensive list of changes in this release
- Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may
now be used within constant expressions.
+- The floating point comparison builtins (``__builtin_isgreater``,
+ ``__builtin_isgreaterequal``, ``__builtin_isless``, etc.) and
+ ``__builtin_signbit`` can now be used in constant expressions.
+
New Compiler Flags
------------------
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and
>From afc47030356bb9ad4ad3e2be720699157a3eb806 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Tue, 4 Jun 2024 19:09:27 +0100
Subject: [PATCH 5/5] Simplifiy comparison function logic
---
clang/lib/AST/ExprConstant.cpp | 71 +++++++++---------------
clang/lib/AST/Interp/InterpBuiltin.cpp | 77 +++++++++-----------------
2 files changed, 51 insertions(+), 97 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index cf715857f13707..7fca3f43ed9938 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12670,51 +12670,32 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
!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);
+ return Success(
+ [&] {
+ switch (BuiltinOp) {
+ case Builtin::BI__builtin_isgreater:
+ return LHS > RHS;
+ case Builtin::BI__builtin_isgreaterequal:
+ return LHS >= RHS;
+ case Builtin::BI__builtin_isless:
+ return LHS < RHS;
+ case Builtin::BI__builtin_islessequal:
+ return LHS <= RHS;
+ case Builtin::BI__builtin_islessgreater: {
+ APFloat::cmpResult cmp = LHS.compare(RHS);
+ return cmp == APFloat::cmpResult::cmpLessThan ||
+ cmp == APFloat::cmpResult::cmpGreaterThan;
+ }
+ case Builtin::BI__builtin_isunordered:
+ return LHS.compare(RHS) == APFloat::cmpResult::cmpUnordered;
+ default:
+ llvm_unreachable("Unexpected builtin ID: Should be a floating "
+ "point comparison function");
+ }
+ }()
+ ? 1
+ : 0,
+ E);
}
case Builtin::BI__builtin_issignaling: {
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 69e2960269c480..9acbd8204275d6 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -446,59 +446,32 @@ static bool interp_floating_comparison(InterpState &S, CodePtr OpPC,
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());
+ pushInteger(
+ S,
+ [&] {
+ switch (ID) {
+ case Builtin::BI__builtin_isgreater:
+ return LHS > RHS;
+ case Builtin::BI__builtin_isgreaterequal:
+ return LHS >= RHS;
+ case Builtin::BI__builtin_isless:
+ return LHS < RHS;
+ case Builtin::BI__builtin_islessequal:
+ return LHS <= RHS;
+ case Builtin::BI__builtin_islessgreater: {
+ ComparisonCategoryResult cmp = LHS.compare(RHS);
+ return cmp == ComparisonCategoryResult::Less ||
+ cmp == ComparisonCategoryResult::Greater;
+ }
+ case Builtin::BI__builtin_isunordered:
+ return LHS.compare(RHS) == ComparisonCategoryResult::Unordered;
+ default:
+ llvm_unreachable("Unexpected builtin ID: Should be a floating point "
+ "comparison function");
+ }
+ }(),
+ Call->getType());
return true;
}
More information about the cfe-commits
mailing list