-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[clang] constexpr integer __builtin_elementwise_{max,min}
#152294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Iris Shi (el-ev) Changes
This PR adds support for the bulitin elementwise max/min functions for integer types. Full diff: https://github.com/llvm/llvm-project/pull/152294.diff 7 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index b5bb198ca637a..5f2e57b5db5df 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -759,7 +759,8 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``,
``__builtin_elementwise_bitreverse``, ``__builtin_elementwise_add_sat``,
-``__builtin_elementwise_sub_sat`` can be called in a ``constexpr`` context.
+``__builtin_elementwise_sub_sat``, ``__builtin_elementwise_max``,
+``__builtin_elementwise_min`` can be called in a ``constexpr`` context.
No implicit promotion of integer types takes place. The mixing of integer types
of different sizes and signs is forbidden in binary and ternary builtins.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a95d1e2736a3..ae5cdb89ef40b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -120,6 +120,9 @@ Non-comprehensive list of changes in this release
This feature is enabled by default but can be disabled by compiling with
``-fno-sanitize-annotate-debug-info-traps``.
+- ``__builtin_elementwise_max`` and ``__builtin_elementwise_min`` functions for integer types can
+ now be used in constant expressions.
+
New Compiler Flags
------------------
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index c81714e9b009d..0181674da89ac 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1300,13 +1300,13 @@ def ElementwiseBitreverse : Builtin {
def ElementwiseMax : Builtin {
let Spellings = ["__builtin_elementwise_max"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
def ElementwiseMin : Builtin {
let Spellings = ["__builtin_elementwise_min"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index f908d02c0412d..3385f4db06f9f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2253,7 +2253,6 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC,
static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC,
const CallExpr *Call,
unsigned BuiltinID) {
- Call->dumpColor();
assert(Call->getNumArgs() == 2);
// Single integer case.
@@ -2320,6 +2319,80 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC,
return true;
}
+static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC,
+ const CallExpr *Call,
+ unsigned BuiltinID) {
+ assert(Call->getNumArgs() == 2);
+
+ QualType Arg0Type = Call->getArg(0)->getType();
+
+ // TODO: Support floating-point types.
+ if (!(Arg0Type->isIntegerType() ||
+ (Arg0Type->isVectorType() &&
+ Arg0Type->castAs<VectorType>()->getElementType()->isIntegerType())))
+ return false;
+
+ if (!Arg0Type->isVectorType()) {
+ assert(!Call->getArg(1)->getType()->isVectorType());
+ APSInt RHS = popToAPSInt(
+ S.Stk, *S.getContext().classify(Call->getArg(1)->getType()));
+ APSInt LHS = popToAPSInt(
+ S.Stk, *S.getContext().classify(Call->getArg(0)->getType()));
+ APInt Result;
+ if (BuiltinID == Builtin::BI__builtin_elementwise_max) {
+ Result = std::max(LHS, RHS);
+ } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) {
+ Result = std::min(LHS, RHS);
+ } else {
+ llvm_unreachable("Wrong builtin ID");
+ }
+
+ pushInteger(S, APSInt(Result, !LHS.isSigned()), Call->getType());
+ return true;
+ }
+
+ // Vector case.
+ assert(Call->getArg(0)->getType()->isVectorType() &&
+ Call->getArg(1)->getType()->isVectorType());
+ const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
+ assert(VT->getElementType() ==
+ Call->getArg(1)->getType()->castAs<VectorType>()->getElementType());
+ assert(VT->getNumElements() ==
+ Call->getArg(1)->getType()->castAs<VectorType>()->getNumElements());
+ assert(VT->getElementType()->isIntegralOrEnumerationType());
+
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+ const Pointer &Dst = S.Stk.peek<Pointer>();
+ PrimType ElemT = *S.getContext().classify(VT->getElementType());
+ unsigned NumElems = VT->getNumElements();
+ for (unsigned I = 0; I != NumElems; ++I) {
+ APSInt Elem1;
+ APSInt Elem2;
+ INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+ Elem1 = LHS.elem<T>(I).toAPSInt();
+ Elem2 = RHS.elem<T>(I).toAPSInt();
+ });
+
+ APSInt Result;
+ if (BuiltinID == Builtin::BI__builtin_elementwise_max) {
+ Result = APSInt(std::max(Elem1, Elem2),
+ Call->getType()->isUnsignedIntegerOrEnumerationType());
+ } else if (BuiltinID == Builtin::BI__builtin_elementwise_min) {
+ Result = APSInt(std::min(Elem1, Elem2),
+ Call->getType()->isUnsignedIntegerOrEnumerationType());
+ } else {
+ llvm_unreachable("Wrong builtin ID");
+ }
+
+ INT_TYPE_SWITCH_NO_BOOL(ElemT,
+ { Dst.elem<T>(I) = static_cast<T>(Result); });
+ }
+ Dst.initializeAllElements();
+
+ return true;
+}
+
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
uint32_t BuiltinID) {
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -2727,6 +2800,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI__builtin_elementwise_sub_sat:
return interp__builtin_elementwise_sat(S, OpPC, Call, BuiltinID);
+ case Builtin::BI__builtin_elementwise_max:
+ case Builtin::BI__builtin_elementwise_min:
+ return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
+
default:
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_invalid_subexpr_in_const_expr)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 34af9ccd2e96e..51ed784b7db85 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11654,6 +11654,41 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
+ case Builtin::BI__builtin_elementwise_max:
+ case Builtin::BI__builtin_elementwise_min: {
+ APValue SourceLHS, SourceRHS;
+ if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) ||
+ !EvaluateAsRValue(Info, E->getArg(1), SourceRHS))
+ return false;
+
+ QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
+
+ if (!DestEltTy->isIntegerType())
+ return false;
+
+ unsigned SourceLen = SourceLHS.getVectorLength();
+ SmallVector<APValue, 4> ResultElements;
+ ResultElements.reserve(SourceLen);
+
+ for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
+ APSInt LHS = SourceLHS.getVectorElt(EltNum).getInt();
+ APSInt RHS = SourceRHS.getVectorElt(EltNum).getInt();
+ switch (E->getBuiltinCallee()) {
+ case Builtin::BI__builtin_elementwise_max:
+ ResultElements.push_back(
+ APValue(APSInt(std::max(LHS, RHS),
+ DestEltTy->isUnsignedIntegerOrEnumerationType())));
+ break;
+ case Builtin::BI__builtin_elementwise_min:
+ ResultElements.push_back(
+ APValue(APSInt(std::min(LHS, RHS),
+ DestEltTy->isUnsignedIntegerOrEnumerationType())));
+ break;
+ }
+ }
+
+ return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+ }
}
}
@@ -13548,7 +13583,24 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
APInt Result = LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS);
return Success(APSInt(Result, !LHS.isSigned()), E);
}
+ case Builtin::BI__builtin_elementwise_max: {
+ APSInt LHS, RHS;
+ if (!EvaluateInteger(E->getArg(0), LHS, Info) ||
+ !EvaluateInteger(E->getArg(1), RHS, Info))
+ return false;
+
+ APInt Result = std::max(LHS, RHS);
+ return Success(APSInt(Result, !LHS.isSigned()), E);
+ }
+ case Builtin::BI__builtin_elementwise_min: {
+ APSInt LHS, RHS;
+ if (!EvaluateInteger(E->getArg(0), LHS, Info) ||
+ !EvaluateInteger(E->getArg(1), RHS, Info))
+ return false;
+ APInt Result = std::min(LHS, RHS);
+ return Success(APSInt(Result, !LHS.isSigned()), E);
+ }
case Builtin::BIstrlen:
case Builtin::BIwcslen:
// A call to strlen is not a constant expression.
diff --git a/clang/test/CodeGen/builtins-elementwise-math.c b/clang/test/CodeGen/builtins-elementwise-math.c
index ee8345ff51e5e..9fd12f53da333 100644
--- a/clang/test/CodeGen/builtins-elementwise-math.c
+++ b/clang/test/CodeGen/builtins-elementwise-math.c
@@ -418,7 +418,7 @@ void test_builtin_elementwise_max(float f1, float f2, double d1, double d2,
// CHECK-NEXT: call i32 @llvm.smax.i32(i32 [[IAS1]], i32 [[B]])
int_as_one = __builtin_elementwise_max(int_as_one, b);
- // CHECK: call i32 @llvm.smax.i32(i32 1, i32 97)
+ // CHECK: store i64 97, ptr [[I1:%.+]], align 8
i1 = __builtin_elementwise_max(1, 'a');
}
@@ -508,6 +508,9 @@ void test_builtin_elementwise_min(float f1, float f2, double d1, double d2,
// CHECK-NEXT: [[B:%.+]] = load i32, ptr @b, align 4
// CHECK-NEXT: call i32 @llvm.smin.i32(i32 [[IAS1]], i32 [[B]])
int_as_one = __builtin_elementwise_min(int_as_one, b);
+
+ // CHECK: store i64 2, ptr [[I1:%.+]], align 8
+ i1 = __builtin_elementwise_min(2, 'b');
}
void test_builtin_elementwise_bitreverse(si8 vi1, si8 vi2,
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index bde5c478b2b6f..bc575dca98d77 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -860,3 +860,19 @@ static_assert(__builtin_elementwise_sub_sat(0U, 1U) == 0U);
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_sub_sat((vector4char){5, 4, 3, 2}, (vector4char){1, 1, 1, 1})) == (LITTLE_END ? 0x01020304 : 0x04030201));
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_sub_sat((vector4uchar){5, 4, 3, 2}, (vector4uchar){1, 1, 1, 1})) == (LITTLE_END ? 0x01020304U : 0x04030201U));
static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_sub_sat((vector4short){(short)0x8000, (short)0x8001, (short)0x8002, (short)0x8003}, (vector4short){7, 8, 9, 10}) == (LITTLE_END ? 0x8000800080008000 : 0x8000800080008000)));
+
+static_assert(__builtin_elementwise_max(1, 2) == 2);
+static_assert(__builtin_elementwise_max(-1, 1) == 1);
+static_assert(__builtin_elementwise_max(1U, 2U) == 2U);
+static_assert(__builtin_elementwise_max(~0U, 0U) == ~0U);
+static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_max((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFF03FE04 : 0x04FE03FF ));
+static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_max((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x04030304U);
+static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_max((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFF0003FFFE0004 : 0x0004FFFE0003FFFF));
+
+static_assert(__builtin_elementwise_min(1, 2) == 1);
+static_assert(__builtin_elementwise_min(-1, 1) == -1);
+static_assert(__builtin_elementwise_min(1U, 2U) == 1U);
+static_assert(__builtin_elementwise_min(~0U, 0U) == 0U);
+static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFC02FD01 : 0x01FD02FC));
+static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x01020201U);
+static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_min((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFC0002FFFD0001 : 0x0001FFFD0002FFFC));
|
case Builtin::BI__builtin_elementwise_max: | ||
case Builtin::BI__builtin_elementwise_min: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cases for max and min look pretty much the same as for add_sat and sub_sat just above. Perhaps it makes sense to simply extend switch
on line 11642?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add/sub_sat only works for integer, but min/max will support floating point numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - but I know very little about clang interp code
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/14438 Here is the relevant piece of the build log for the reference
|
This PR adds support for the bulitin elementwise max/min functions for integer types.