diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3c6c97bb1fa10..fac78a060b44f 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -875,12 +875,14 @@ of different sizes and signs is forbidden in binary and ternary builtins. for the comparison. T __builtin_elementwise_fshl(T x, T y, T z) perform a funnel shift left. Concatenate x and y (x is the most integer types significant bits of the wide value), the combined value is shifted - left by z, and the most significant bits are extracted to produce + left by z (modulo the bit width of the original arguments), + and the most significant bits are extracted to produce a result that is the same size as the original arguments. T __builtin_elementwise_fshr(T x, T y, T z) perform a funnel shift right. Concatenate x and y (x is the most integer types significant bits of the wide value), the combined value is shifted - right by z, and the least significant bits are extracted to produce + right by z (modulo the bit width of the original arguments), + and the least significant bits are extracted to produce a result that is the same size as the original arguments. T __builtin_elementwise_ctlz(T x[, T y]) return the number of leading 0 bits in the first argument. If integer types the first argument is 0 and an optional second argument is provided, diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 6d21c620bfc80..6f891f6761f1c 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1528,13 +1528,13 @@ def ElementwiseSubSat : Builtin { def ElementwiseFshl : Builtin { let Spellings = ["__builtin_elementwise_fshl"]; - let Attributes = [NoThrow, Const, CustomTypeChecking]; + let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr]; let Prototype = "void(...)"; } def ElementwiseFshr : Builtin { let Spellings = ["__builtin_elementwise_fshr"]; - 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 2e28814abfddb..4a99231f54349 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -2817,6 +2817,78 @@ static bool interp__builtin_select(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC, + const CallExpr *Call, + unsigned BuiltinID) { + assert(Call->getNumArgs() == 3); + + QualType Arg0Type = Call->getArg(0)->getType(); + QualType Arg1Type = Call->getArg(1)->getType(); + QualType Arg2Type = Call->getArg(2)->getType(); + + // Non-vector integer types. + if (!Arg0Type->isVectorType()) { + assert(!Arg1Type->isVectorType()); + assert(!Arg2Type->isVectorType()); + const APSInt &Shift = + popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type)); + const APSInt &Lo = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type)); + const APSInt &Hi = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type)); + APSInt Result; + if (BuiltinID == Builtin::BI__builtin_elementwise_fshl) + Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()); + else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) + Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()); + else + llvm_unreachable("Wrong builtin ID"); + pushInteger(S, Result, Call->getType()); + return true; + } + + // Vector type. + assert(Arg0Type->isVectorType() && Arg1Type->isVectorType() && + Arg2Type->isVectorType()); + + const auto *VecT = Arg0Type->castAs(); + const PrimType &ElemT = *S.getContext().classify(VecT->getElementType()); + unsigned NumElems = VecT->getNumElements(); + + assert(VecT->getElementType() == + Arg1Type->castAs()->getElementType() && + VecT->getElementType() == + Arg2Type->castAs()->getElementType()); + assert(NumElems == Arg1Type->castAs()->getNumElements() && + NumElems == Arg2Type->castAs()->getNumElements()); + assert(VecT->getElementType()->isIntegralOrEnumerationType()); + + const Pointer &VecShift = S.Stk.pop(); + const Pointer &VecLo = S.Stk.pop(); + const Pointer &VecHi = S.Stk.pop(); + const Pointer &Dst = S.Stk.peek(); + for (unsigned I = 0; I != NumElems; ++I) { + APSInt Hi; + APSInt Lo; + APSInt Shift; + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Hi = VecHi.elem(I).toAPSInt(); + Lo = VecLo.elem(I).toAPSInt(); + Shift = VecShift.elem(I).toAPSInt(); + }); + APSInt Result; + if (BuiltinID == Builtin::BI__builtin_elementwise_fshl) + Result = APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()); + else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) + Result = APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()); + else + llvm_unreachable("Wrong builtin ID"); + INT_TYPE_SWITCH_NO_BOOL(ElemT, + { Dst.elem(I) = static_cast(Result); }); + } + Dst.initializeAllElements(); + + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, uint32_t BuiltinID) { if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID)) @@ -3279,6 +3351,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case X86::BI__builtin_ia32_selectpd_512: return interp__builtin_select(S, OpPC, Call); + case Builtin::BI__builtin_elementwise_fshl: + case Builtin::BI__builtin_elementwise_fshr: + return interp__builtin_elementwise_fsh(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 9b934753bcc3c..942659353f20c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -11941,6 +11941,40 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) { } return Success(APValue(ResultElements.data(), ResultElements.size()), E); } + + case Builtin::BI__builtin_elementwise_fshl: + case Builtin::BI__builtin_elementwise_fshr: { + APValue SourceHi, SourceLo, SourceShift; + if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) || + !EvaluateAsRValue(Info, E->getArg(1), SourceLo) || + !EvaluateAsRValue(Info, E->getArg(2), SourceShift)) + return false; + + QualType DestEltTy = E->getType()->castAs()->getElementType(); + if (!DestEltTy->isIntegerType()) + return false; + + unsigned SourceLen = SourceHi.getVectorLength(); + SmallVector ResultElements; + ResultElements.reserve(SourceLen); + for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) { + const APSInt &Hi = SourceHi.getVectorElt(EltNum).getInt(); + const APSInt &Lo = SourceLo.getVectorElt(EltNum).getInt(); + const APSInt &Shift = SourceShift.getVectorElt(EltNum).getInt(); + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_elementwise_fshl: + ResultElements.push_back(APValue( + APSInt(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()))); + break; + case Builtin::BI__builtin_elementwise_fshr: + ResultElements.push_back(APValue( + APSInt(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()))); + break; + } + } + + return Success(APValue(ResultElements.data(), ResultElements.size()), E); + } } } @@ -13879,6 +13913,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, APInt Result = std::min(LHS, RHS); return Success(APSInt(Result, !LHS.isSigned()), E); } + case Builtin::BI__builtin_elementwise_fshl: + case Builtin::BI__builtin_elementwise_fshr: { + APSInt Hi, Lo, Shift; + if (!EvaluateInteger(E->getArg(0), Hi, Info) || + !EvaluateInteger(E->getArg(1), Lo, Info) || + !EvaluateInteger(E->getArg(2), Shift, Info)) + return false; + + switch (BuiltinOp) { + case Builtin::BI__builtin_elementwise_fshl: { + APSInt Result(llvm::APIntOps::fshl(Hi, Lo, Shift), Hi.isUnsigned()); + return Success(Result, E); + } + case Builtin::BI__builtin_elementwise_fshr: { + APSInt Result(llvm::APIntOps::fshr(Hi, Lo, Shift), Hi.isUnsigned()); + return Success(Result, E); + } + } + } case Builtin::BIstrlen: case Builtin::BIwcslen: // A call to strlen is not a constant expression. diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp index 17fa95871740d..714a7fb753214 100644 --- a/clang/test/Sema/constant-builtins-vector.cpp +++ b/clang/test/Sema/constant-builtins-vector.cpp @@ -961,3 +961,51 @@ static_assert(fmaDouble1[3] == 26.0); constexpr float fmaArray[] = {2.0f, 2.0f, 2.0f, 2.0f}; constexpr float fmaResult = __builtin_elementwise_fma(fmaArray[1], fmaArray[2], fmaArray[3]); static_assert(fmaResult == 6.0f, ""); + +static_assert(__builtin_elementwise_fshl((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)255); +static_assert(__builtin_elementwise_fshl((char)127, (char)0, (char)8) == (char)127); +static_assert(__builtin_elementwise_fshl((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)0); +static_assert(__builtin_elementwise_fshl((char)0, (char)127, (char)8) == (char)0); +static_assert(__builtin_elementwise_fshr((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)0); +static_assert(__builtin_elementwise_fshr((char)127, (char)0, (char)8) == (char)0); +static_assert(__builtin_elementwise_fshr((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)255); +static_assert(__builtin_elementwise_fshr((char)0, (char)127, (char)8) == (char)127); +static_assert(__builtin_elementwise_fshl((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)4294967295); +static_assert(__builtin_elementwise_fshl((int)2147483647, (int)0, (int)32) == (int)2147483647); +static_assert(__builtin_elementwise_fshl((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)0); +static_assert(__builtin_elementwise_fshl((int)0, (int)2147483647, (int)32) == (int)0); +static_assert(__builtin_elementwise_fshr((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)0); +static_assert(__builtin_elementwise_fshr((int)2147483647, (int)0, (int)32) == (int)0); +static_assert(__builtin_elementwise_fshr((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)4294967295); +static_assert(__builtin_elementwise_fshr((int)0, (int)2147483647, (int)32) == (int)2147483647); +static_assert(__builtin_elementwise_fshl((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL); +static_assert(__builtin_elementwise_fshl((long long)9223372036854775807, (long long)0, (long long)64) == (long long)9223372036854775807); +static_assert(__builtin_elementwise_fshl((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)0); +static_assert(__builtin_elementwise_fshl((long long)0, (long long)9223372036854775807, (long long)64) == (long long)0); +static_assert(__builtin_elementwise_fshr((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)0); +static_assert(__builtin_elementwise_fshr((long long)9223372036854775807, (long long)0, (long long)64) == (long long)0); +static_assert(__builtin_elementwise_fshr((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL); +static_assert(__builtin_elementwise_fshr((long long)0, (long long)9223372036854775807, (long long)64) == (long long)9223372036854775807); +static_assert(__builtin_elementwise_fshl((short) 1, (short) 2, (short) 3) == (short)8); +static_assert(__builtin_elementwise_fshl((short) 2, (short) 1, (short) 3) == (short)16); +static_assert(__builtin_elementwise_fshl(1, 2 , 2) == 4); +static_assert(__builtin_elementwise_fshl(2L, 1L , 2L) == 8L); +static_assert(__builtin_elementwise_fshr((unsigned char)1, (unsigned char)2, (unsigned char)3) == (unsigned char)32); +constexpr vector4uchar v4s_fshl_var = + __builtin_elementwise_fshl((vector4uchar){255, 15, 0, 2}, + (vector4uchar){0, 15, 255, 1}, + (vector4uchar){15, 11, 8, 3}); +static_assert(v4s_fshl_var[0] == 128); +static_assert(v4s_fshl_var[1] == 120); +static_assert(v4s_fshl_var[2] == 0); +static_assert(v4s_fshl_var[3] == 16); +constexpr vector4uchar v4s_fshr_var = + __builtin_elementwise_fshr((vector4uchar){255, 15, 0, 1}, + (vector4uchar){0, 15, 255, 2}, + (vector4uchar){15, 11, 8, 3}); +static_assert(v4s_fshr_var[0] == 254); +static_assert(v4s_fshr_var[1] == 225); +static_assert(v4s_fshr_var[2] == 255); +static_assert(v4s_fshr_var[3] == 32); +static_assert(__builtin_elementwise_fshl(v4s_fshl_var[0], v4s_fshl_var[1], v4s_fshl_var[2]) == 128); +static_assert(__builtin_elementwise_fshr(v4s_fshr_var[0], v4s_fshr_var[1], v4s_fshr_var[2]) == 253);