Author: Iris Shi Date: 2025-08-12T09:49:05+08:00 New Revision: 713ec58dec1ec4d3866d62e563d7bd8e1ae3da53
URL: https://github.com/llvm/llvm-project/commit/713ec58dec1ec4d3866d62e563d7bd8e1ae3da53 DIFF: https://github.com/llvm/llvm-project/commit/713ec58dec1ec4d3866d62e563d7bd8e1ae3da53.diff LOG: [clang] constexpr integer `__builtin_elementwise_{max,min}` (#152294) - Closes #152278 - Part of #51787 This PR adds support for the bulitin elementwise max/min functions for integer types. Added: Modified: clang/docs/LanguageExtensions.rst clang/docs/ReleaseNotes.rst clang/include/clang/Basic/Builtins.td clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp clang/test/CodeGen/builtins-elementwise-math.c clang/test/Sema/constant-builtins-vector.cpp Removed: ################################################################################ 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 diff erent sizes and signs is forbidden in binary and ternary builtins. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index fe6a48117df29..29eb18bcf85fd 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -124,6 +124,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 ad923a52a4ce1..307b77846969f 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -2259,7 +2259,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. @@ -2326,6 +2325,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)) @@ -2733,6 +2806,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 5cf2b9a0355ab..36dd0f5d7a065 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -11688,6 +11688,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); + } } } @@ -13585,7 +13620,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)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits