diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 1c4a6ecca2142..719b7a80cdbca 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -647,42 +647,35 @@ elementwise to the input. Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±infinity -=========================================== ================================================================ ========================================= - Name Operation Supported element types -=========================================== ================================================================ ========================================= - T __builtin_elementwise_abs(T x) return the absolute value of a number x; the absolute value of signed integer and floating point types - the most negative integer remains the most negative integer - T __builtin_elementwise_fma(T x, T y, T z) fused multiply add, (x * y) + z. floating point types - T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types - T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types - T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types - T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types - T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types - T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types - T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types - T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types - T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types - T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types - T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types - T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types - T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types - T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types - T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types - - T __builtin_elementwise_sqrt(T x) return the square root of a floating-point number floating point types - T __builtin_elementwise_roundeven(T x) round x to the nearest integer value in floating point format, floating point types - rounding halfway cases to even (that is, to the nearest value - that is an even integer), regardless of the current rounding - direction. - T __builtin_elementwise_round(T x) round x to the nearest integer value in floating point format, floating point types - rounding halfway cases away from zero, regardless of the - current rounding direction. May raise floating-point - exceptions. - T __builtin_elementwise_trunc(T x) return the integral value nearest to but no larger in floating point types - magnitude than x +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. + +============================================== ====================================================================== ========================================= + Name Operation Supported element types +============================================== ====================================================================== ========================================= + T __builtin_elementwise_abs(T x) return the absolute value of a number x; the absolute value of signed integer and floating point types + the most negative integer remains the most negative integer + T __builtin_elementwise_fma(T x, T y, T z) fused multiply add, (x * y) + z. floating point types + T __builtin_elementwise_ceil(T x) return the smallest integral value greater than or equal to x floating point types + T __builtin_elementwise_sin(T x) return the sine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_cos(T x) return the cosine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_tan(T x) return the tangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_asin(T x) return the arcsine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_acos(T x) return the arccosine of x interpreted as an angle in radians floating point types + T __builtin_elementwise_atan(T x) return the arctangent of x interpreted as an angle in radians floating point types + T __builtin_elementwise_atan2(T y, T x) return the arctangent of y/x floating point types + T __builtin_elementwise_sinh(T x) return the hyperbolic sine of angle x in radians floating point types + T __builtin_elementwise_cosh(T x) return the hyperbolic cosine of angle x in radians floating point types + T __builtin_elementwise_tanh(T x) return the hyperbolic tangent of angle x in radians floating point types + T __builtin_elementwise_floor(T x) return the largest integral value less than or equal to x floating point types + T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types + T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types + T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types + T __builtin_elementwise_popcount(T x) return the number of 1 bits in x integer types + T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types + T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types + T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types + T __builtin_elementwise_exp2(T x) returns the base-2 exponential, 2^x, of the specified value floating point types T __builtin_elementwise_nearbyint(T x) round x to the nearest integer value in floating point format, floating point types rounding according to the current rounding direction. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a38450b68fffd..79610dda0b4f6 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2551,7 +2551,9 @@ class Sema final : public SemaBase { bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto); - bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res); + /// \param FPOnly restricts the arguments to floating-point types. + std::optional BuiltinVectorMath(CallExpr *TheCall, + bool FPOnly = false); bool BuiltinVectorToScalarMath(CallExpr *TheCall); /// Handles the checks for format strings, non-POD arguments to vararg @@ -2762,8 +2764,9 @@ class Sema final : public SemaBase { ExprResult AtomicOpsOverloaded(ExprResult TheCallResult, AtomicExpr::AtomicOp Op); - bool BuiltinElementwiseMath(CallExpr *TheCall); - bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall); + bool BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly); + bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall, + bool FPOnly = false); bool BuiltinNonDeterministicValue(CallExpr *TheCall); @@ -7751,10 +7754,15 @@ class Sema final : public SemaBase { return K == ConditionKind::Switch ? Context.IntTy : Context.BoolTy; } - // UsualUnaryConversions - promotes integers (C99 6.3.1.1p2) and converts - // functions and arrays to their respective pointers (C99 6.3.2.1). + // UsualUnaryConversions - promotes integers (C99 6.3.1.1p2), converts + // functions and arrays to their respective pointers (C99 6.3.2.1), and + // promotes floating-piont types according to the language semantics. ExprResult UsualUnaryConversions(Expr *E); + // UsualUnaryFPConversions - promotes floating-point types according to the + // current language semantics. + ExprResult UsualUnaryFPConversions(Expr *E); + /// CallExprUnaryConversions - a special case of an unary conversion /// performed on a function designator of a call expression. ExprResult CallExprUnaryConversions(Expr *E); @@ -7829,6 +7837,11 @@ class Sema final : public SemaBase { ExprResult DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, FunctionDecl *FDecl); + // Check that the usual arithmetic conversions can be performed on this pair + // of expressions that might be of enumeration type. + void checkEnumArithmeticConversions(Expr *LHS, Expr *RHS, SourceLocation Loc, + Sema::ArithConvKind ACK); + // UsualArithmeticConversions - performs the UsualUnaryConversions on it's // operands and then handles various conversions that are common to binary // operators (C99 6.3.1.8). If both operands aren't arithmetic, this diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 33629bc2eaddf..08d65a33176ba 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2711,7 +2711,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, // These builtins restrict the element type to floating point // types only, and take in two arguments. case Builtin::BI__builtin_elementwise_pow: { - if (BuiltinElementwiseMath(TheCall)) + if (BuiltinElementwiseMath(TheCall, true)) return ExprError(); QualType ArgTy = TheCall->getArg(0)->getType(); @@ -2727,7 +2727,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, // types only. case Builtin::BI__builtin_elementwise_add_sat: case Builtin::BI__builtin_elementwise_sub_sat: { - if (BuiltinElementwiseMath(TheCall)) + if (BuiltinElementwiseMath(TheCall, false)) return ExprError(); const Expr *Arg = TheCall->getArg(0); @@ -2747,7 +2747,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, case Builtin::BI__builtin_elementwise_min: case Builtin::BI__builtin_elementwise_max: - if (BuiltinElementwiseMath(TheCall)) + if (BuiltinElementwiseMath(TheCall, false)) return ExprError(); break; @@ -14899,11 +14899,23 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) { _2, _3, _4)); } +// Performs a similar job to Sema::UsualUnaryConversions, but without any +// implicit promotion of integral/enumeration types. +static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) { + // First, convert to an r-value. + ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E); + if (Res.isInvalid()) + return ExprError(); + + // Promote floating-point types. + return S.UsualUnaryFPConversions(Res.get()); +} + bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) { if (checkArgCount(TheCall, 1)) return true; - ExprResult A = UsualUnaryConversions(TheCall->getArg(0)); + ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0)); if (A.isInvalid()) return true; @@ -14917,53 +14929,78 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) { return false; } -bool Sema::BuiltinElementwiseMath(CallExpr *TheCall) { - QualType Res; - if (BuiltinVectorMath(TheCall, Res)) - return true; - TheCall->setType(Res); - return false; +bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) { + if (auto Res = BuiltinVectorMath(TheCall, FPOnly); Res.has_value()) { + TheCall->setType(*Res); + return false; + } + return true; } bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) { - QualType Res; - if (BuiltinVectorMath(TheCall, Res)) + std::optional Res = BuiltinVectorMath(TheCall); + if (!Res) return true; - if (auto *VecTy0 = Res->getAs()) + if (auto *VecTy0 = (*Res)->getAs()) TheCall->setType(VecTy0->getElementType()); else - TheCall->setType(Res); + TheCall->setType(*Res); return false; } -bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res) { +static bool checkBuiltinVectorMathMixedEnums(Sema &S, Expr *LHS, Expr *RHS, + SourceLocation Loc) { + QualType L = LHS->getEnumCoercedType(S.Context), + R = RHS->getEnumCoercedType(S.Context); + if (L->isUnscopedEnumerationType() && R->isUnscopedEnumerationType() && + !S.Context.hasSameUnqualifiedType(L, R)) { + return S.Diag(Loc, diag::err_conv_mixed_enum_types_cxx26) + << LHS->getSourceRange() << RHS->getSourceRange() + << /*Arithmetic Between*/ 0 << L << R; + } + return false; +} + +std::optional Sema::BuiltinVectorMath(CallExpr *TheCall, + bool FPOnly) { if (checkArgCount(TheCall, 2)) - return true; + return std::nullopt; - ExprResult A = TheCall->getArg(0); - ExprResult B = TheCall->getArg(1); - // Do standard promotions between the two arguments, returning their common - // type. - Res = UsualArithmeticConversions(A, B, TheCall->getExprLoc(), ACK_Comparison); - if (A.isInvalid() || B.isInvalid()) - return true; + if (checkBuiltinVectorMathMixedEnums( + *this, TheCall->getArg(0), TheCall->getArg(1), TheCall->getExprLoc())) + return std::nullopt; - QualType TyA = A.get()->getType(); - QualType TyB = B.get()->getType(); + Expr *Args[2]; + for (int I = 0; I < 2; ++I) { + ExprResult Converted = + BuiltinVectorMathConversions(*this, TheCall->getArg(I)); + if (Converted.isInvalid()) + return std::nullopt; + Args[I] = Converted.get(); + } - if (Res.isNull() || TyA.getCanonicalType() != TyB.getCanonicalType()) - return Diag(A.get()->getBeginLoc(), - diag::err_typecheck_call_different_arg_types) - << TyA << TyB; + SourceLocation LocA = Args[0]->getBeginLoc(); + QualType TyA = Args[0]->getType(); + QualType TyB = Args[1]->getType(); - if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1)) - return true; + if (TyA.getCanonicalType() != TyB.getCanonicalType()) { + Diag(LocA, diag::err_typecheck_call_different_arg_types) << TyA << TyB; + return std::nullopt; + } - TheCall->setArg(0, A.get()); - TheCall->setArg(1, B.get()); - return false; + if (FPOnly) { + if (checkFPMathBuiltinElementType(*this, LocA, TyA, 1)) + return std::nullopt; + } else { + if (checkMathBuiltinElementType(*this, LocA, TyA, 1)) + return std::nullopt; + } + + TheCall->setArg(0, Args[0]); + TheCall->setArg(1, Args[1]); + return TyA; } bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall, @@ -14971,9 +15008,17 @@ bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall, if (checkArgCount(TheCall, 3)) return true; + SourceLocation Loc = TheCall->getExprLoc(); + if (checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(0), + TheCall->getArg(1), Loc) || + checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(1), + TheCall->getArg(2), Loc)) + return true; + Expr *Args[3]; for (int I = 0; I < 3; ++I) { - ExprResult Converted = UsualUnaryConversions(TheCall->getArg(I)); + ExprResult Converted = + BuiltinVectorMathConversions(*this, TheCall->getArg(I)); if (Converted.isInvalid()) return true; Args[I] = Converted.get(); @@ -15010,7 +15055,7 @@ bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall, return false; } -bool Sema::PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall) { +bool Sema::PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall, bool FPOnly) { if (checkArgCount(TheCall, 1)) return true; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 03210b75ed814..564c4e56e000b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1205,20 +1205,11 @@ ExprResult Sema::CallExprUnaryConversions(Expr *E) { return Res.get(); } -/// UsualUnaryConversions - Performs various conversions that are common to most -/// operators (C99 6.3). The conversions of array and function types are -/// sometimes suppressed. For example, the array->pointer conversion doesn't -/// apply if the array is an argument to the sizeof or address (&) operators. -/// In these instances, this routine should *not* be called. -ExprResult Sema::UsualUnaryConversions(Expr *E) { - // First, convert to an r-value. - ExprResult Res = DefaultFunctionArrayLvalueConversion(E); - if (Res.isInvalid()) - return ExprError(); - E = Res.get(); - +/// UsualUnaryFPConversions - Promotes floating-point types according to the +/// current language semantics. +ExprResult Sema::UsualUnaryFPConversions(Expr *E) { QualType Ty = E->getType(); - assert(!Ty.isNull() && "UsualUnaryConversions - missing type"); + assert(!Ty.isNull() && "UsualUnaryFPConversions - missing type"); LangOptions::FPEvalMethodKind EvalMethod = CurFPFeatures.getFPEvalMethod(); if (EvalMethod != LangOptions::FEM_Source && Ty->isFloatingType() && @@ -1256,7 +1247,30 @@ ExprResult Sema::UsualUnaryConversions(Expr *E) { // Half FP have to be promoted to float unless it is natively supported if (Ty->isHalfType() && !getLangOpts().NativeHalfType) - return ImpCastExprToType(Res.get(), Context.FloatTy, CK_FloatingCast); + return ImpCastExprToType(E, Context.FloatTy, CK_FloatingCast); + + return E; +} + +/// UsualUnaryConversions - Performs various conversions that are common to most +/// operators (C99 6.3). The conversions of array and function types are +/// sometimes suppressed. For example, the array->pointer conversion doesn't +/// apply if the array is an argument to the sizeof or address (&) operators. +/// In these instances, this routine should *not* be called. +ExprResult Sema::UsualUnaryConversions(Expr *E) { + // First, convert to an r-value. + ExprResult Res = DefaultFunctionArrayLvalueConversion(E); + if (Res.isInvalid()) + return ExprError(); + + // Promote floating-point types. + Res = UsualUnaryFPConversions(Res.get()); + if (Res.isInvalid()) + return ExprError(); + E = Res.get(); + + QualType Ty = E->getType(); + assert(!Ty.isNull() && "UsualUnaryConversions - missing type"); // Try to perform integral promotions if the object has a theoretically // promotable type. @@ -1921,9 +1935,9 @@ static QualType handleFixedPointConversion(Sema &S, QualType LHSTy, /// Check that the usual arithmetic conversions can be performed on this pair of /// expressions that might be of enumeration type. -static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS, - SourceLocation Loc, - Sema::ArithConvKind ACK) { +void Sema::checkEnumArithmeticConversions(Expr *LHS, Expr *RHS, + SourceLocation Loc, + Sema::ArithConvKind ACK) { // C++2a [expr.arith.conv]p1: // If one operand is of enumeration type and the other operand is of a // different enumeration type or a floating-point type, this behavior is @@ -1931,54 +1945,53 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS, // // Warn on this in all language modes. Produce a deprecation warning in C++20. // Eventually we will presumably reject these cases (in C++23 onwards?). - QualType L = LHS->getEnumCoercedType(S.Context), - R = RHS->getEnumCoercedType(S.Context); + QualType L = LHS->getEnumCoercedType(Context), + R = RHS->getEnumCoercedType(Context); bool LEnum = L->isUnscopedEnumerationType(), REnum = R->isUnscopedEnumerationType(); bool IsCompAssign = ACK == Sema::ACK_CompAssign; if ((!IsCompAssign && LEnum && R->isFloatingType()) || (REnum && L->isFloatingType())) { - S.Diag(Loc, S.getLangOpts().CPlusPlus26 - ? diag::err_arith_conv_enum_float_cxx26 - : S.getLangOpts().CPlusPlus20 - ? diag::warn_arith_conv_enum_float_cxx20 - : diag::warn_arith_conv_enum_float) + Diag(Loc, getLangOpts().CPlusPlus26 ? diag::err_arith_conv_enum_float_cxx26 + : getLangOpts().CPlusPlus20 + ? diag::warn_arith_conv_enum_float_cxx20 + : diag::warn_arith_conv_enum_float) << LHS->getSourceRange() << RHS->getSourceRange() << (int)ACK << LEnum << L << R; } else if (!IsCompAssign && LEnum && REnum && - !S.Context.hasSameUnqualifiedType(L, R)) { + !Context.hasSameUnqualifiedType(L, R)) { unsigned DiagID; // In C++ 26, usual arithmetic conversions between 2 different enum types // are ill-formed. - if (S.getLangOpts().CPlusPlus26) + if (getLangOpts().CPlusPlus26) DiagID = diag::err_conv_mixed_enum_types_cxx26; else if (!L->castAs()->getDecl()->hasNameForLinkage() || !R->castAs()->getDecl()->hasNameForLinkage()) { // If either enumeration type is unnamed, it's less likely that the // user cares about this, but this situation is still deprecated in // C++2a. Use a different warning group. - DiagID = S.getLangOpts().CPlusPlus20 - ? diag::warn_arith_conv_mixed_anon_enum_types_cxx20 - : diag::warn_arith_conv_mixed_anon_enum_types; + DiagID = getLangOpts().CPlusPlus20 + ? diag::warn_arith_conv_mixed_anon_enum_types_cxx20 + : diag::warn_arith_conv_mixed_anon_enum_types; } else if (ACK == Sema::ACK_Conditional) { // Conditional expressions are separated out because they have // historically had a different warning flag. - DiagID = S.getLangOpts().CPlusPlus20 + DiagID = getLangOpts().CPlusPlus20 ? diag::warn_conditional_mixed_enum_types_cxx20 : diag::warn_conditional_mixed_enum_types; } else if (ACK == Sema::ACK_Comparison) { // Comparison expressions are separated out because they have // historically had a different warning flag. - DiagID = S.getLangOpts().CPlusPlus20 + DiagID = getLangOpts().CPlusPlus20 ? diag::warn_comparison_mixed_enum_types_cxx20 : diag::warn_comparison_mixed_enum_types; } else { - DiagID = S.getLangOpts().CPlusPlus20 + DiagID = getLangOpts().CPlusPlus20 ? diag::warn_arith_conv_mixed_enum_types_cxx20 : diag::warn_arith_conv_mixed_enum_types; } - S.Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange() - << (int)ACK << L << R; + Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange() + << (int)ACK << L << R; } } @@ -1989,7 +2002,7 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS, QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, ArithConvKind ACK) { - checkEnumArithmeticConversions(*this, LHS.get(), RHS.get(), Loc, ACK); + checkEnumArithmeticConversions(LHS.get(), RHS.get(), Loc, ACK); if (ACK != ACK_CompAssign) { LHS = UsualUnaryConversions(LHS.get()); diff --git a/clang/test/CodeGen/builtins-elementwise-math.c b/clang/test/CodeGen/builtins-elementwise-math.c index 8fb52992c0fe6..97e98f4d1d629 100644 --- a/clang/test/CodeGen/builtins-elementwise-math.c +++ b/clang/test/CodeGen/builtins-elementwise-math.c @@ -14,10 +14,18 @@ __attribute__((address_space(1))) int int_as_one; typedef int bar; bar b; +struct StructWithBitfield { + int i : 5; + short s : 3; + char c: 2; + long long int lli : 3; +}; + void test_builtin_elementwise_abs(float f1, float f2, double d1, double d2, float4 vf1, float4 vf2, si8 vi1, si8 vi2, long long int i1, long long int i2, short si, - _BitInt(31) bi1, _BitInt(31) bi2) { + _BitInt(31) bi1, _BitInt(31) bi2, int i, + char ci) { // CHECK-LABEL: define void @test_builtin_elementwise_abs( // CHECK: [[F1:%.+]] = load float, ptr %f1.addr, align 4 // CHECK-NEXT: call float @llvm.fabs.f32(float [[F1]]) @@ -35,6 +43,11 @@ void test_builtin_elementwise_abs(float f1, float f2, double d1, double d2, // CHECK-NEXT: call i64 @llvm.abs.i64(i64 [[I1]], i1 false) i2 = __builtin_elementwise_abs(i1); + // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 + // CHECK: [[S1:%.+]] = trunc i64 [[I1]] to i16 + // CHECK-NEXT: call i16 @llvm.abs.i16(i16 [[S1]], i1 false) + i1 = __builtin_elementwise_abs((short)i1); + // CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16 // CHECK-NEXT: call <8 x i16> @llvm.abs.v8i16(<8 x i16> [[VI1]], i1 false) vi2 = __builtin_elementwise_abs(vi1); @@ -57,10 +70,37 @@ void test_builtin_elementwise_abs(float f1, float f2, double d1, double d2, b = __builtin_elementwise_abs(-10); // CHECK: [[SI:%.+]] = load i16, ptr %si.addr, align 2 - // CHECK-NEXT: [[SI_EXT:%.+]] = sext i16 [[SI]] to i32 - // CHECK-NEXT: [[RES:%.+]] = call i32 @llvm.abs.i32(i32 [[SI_EXT]], i1 false) - // CHECK-NEXT: = trunc i32 [[RES]] to i16 + // CHECK-NEXT: [[RES:%.+]] = call i16 @llvm.abs.i16(i16 [[SI]], i1 false) si = __builtin_elementwise_abs(si); + + struct StructWithBitfield t; + + // CHECK: [[BFLOAD:%.+]] = load i16, ptr %t, align 8 + // CHECK-NEXT: [[BFSHL:%.+]] = shl i16 [[BFLOAD]], 11 + // CHECK-NEXT: [[BFASHR:%.+]] = ashr i16 [[BFSHL]], 11 + // CHECK-NEXT: [[BFCAST:%.+]] = sext i16 [[BFASHR]] to i32 + // CHECK-NEXT: [[RES:%.+]] = call i32 @llvm.abs.i32(i32 [[BFCAST]], i1 false) + i = __builtin_elementwise_abs(t.i); + + // CHECK: [[BFLOAD:%.+]] = load i16, ptr %t, align 8 + // CHECK-NEXT: [[BFSHL:%.+]] = shl i16 [[BFLOAD]], 8 + // CHECK-NEXT: [[BFASHR:%.+]] = ashr i16 [[BFSHL]], 13 + // CHECK-NEXT: [[RES:%.+]] = call i16 @llvm.abs.i16(i16 [[BFASHR]], i1 false) + si = __builtin_elementwise_abs(t.s); + + // CHECK: [[BFLOAD:%.+]] = load i16, ptr %t, align 8 + // CHECK-NEXT: [[BFSHL:%.+]] = shl i16 [[BFLOAD]], 6 + // CHECK-NEXT: [[BFASHR:%.+]] = ashr i16 [[BFSHL]], 14 + // CHECK-NEXT: [[BFCAST:%.+]] = trunc i16 [[BFASHR]] to i8 + // CHECK-NEXT: [[RES:%.+]] = call i8 @llvm.abs.i8(i8 [[BFCAST]], i1 false) + ci = __builtin_elementwise_abs(t.c); + + // CHECK: [[BFLOAD:%.+]] = load i16, ptr %t, align 8 + // CHECK-NEXT: [[BFSHL:%.+]] = shl i16 [[BFLOAD]], 3 + // CHECK-NEXT: [[BFASHR:%.+]] = ashr i16 [[BFSHL]], 13 + // CHECK-NEXT: [[BFCAST:%.+]] = sext i16 [[BFASHR]] to i64 + // CHECK-NEXT: [[RES:%.+]] = call i64 @llvm.abs.i64(i64 [[BFCAST]], i1 false) + i1 = __builtin_elementwise_abs(t.lli); } void test_builtin_elementwise_add_sat(float f1, float f2, double d1, double d2, @@ -68,7 +108,10 @@ void test_builtin_elementwise_add_sat(float f1, float f2, double d1, double d2, long long int i2, si8 vi1, si8 vi2, unsigned u1, unsigned u2, u4 vu1, u4 vu2, _BitInt(31) bi1, _BitInt(31) bi2, - unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2) { + unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2, + char c1, char c2, unsigned char uc1, + unsigned char uc2, short s1, short s2, + unsigned short us1, unsigned short us2) { // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8 // CHECK-NEXT: call i64 @llvm.sadd.sat.i64(i64 [[I1]], i64 [[I2]]) @@ -76,7 +119,7 @@ void test_builtin_elementwise_add_sat(float f1, float f2, double d1, double d2, // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 // CHECK-NEXT: call i64 @llvm.sadd.sat.i64(i64 [[I1]], i64 10) - i1 = __builtin_elementwise_add_sat(i1, 10); + i1 = __builtin_elementwise_add_sat(i1, 10ll); // CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16 // CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, ptr %vi2.addr, align 16 @@ -114,6 +157,33 @@ void test_builtin_elementwise_add_sat(float f1, float f2, double d1, double d2, // CHECK: call i32 @llvm.sadd.sat.i32(i32 1, i32 97) i1 = __builtin_elementwise_add_sat(1, 'a'); + + // CHECK: [[C1:%.+]] = load i8, ptr %c1.addr, align 1 + // CHECK-NEXT: [[C2:%.+]] = load i8, ptr %c2.addr, align 1 + // CHECK-NEXT: call i8 @llvm.sadd.sat.i8(i8 [[C1]], i8 [[C2]]) + c1 = __builtin_elementwise_add_sat(c1, c2); + + // CHECK: [[UC1:%.+]] = load i8, ptr %uc1.addr, align 1 + // CHECK-NEXT: [[UC2:%.+]] = load i8, ptr %uc2.addr, align 1 + // CHECK-NEXT: call i8 @llvm.uadd.sat.i8(i8 [[UC1]], i8 [[UC2]]) + uc1 = __builtin_elementwise_add_sat(uc1, uc2); + + // CHECK: [[S1:%.+]] = load i16, ptr %s1.addr, align 2 + // CHECK-NEXT: [[S2:%.+]] = load i16, ptr %s2.addr, align 2 + // CHECK-NEXT: call i16 @llvm.sadd.sat.i16(i16 [[S1]], i16 [[S2]]) + s1 = __builtin_elementwise_add_sat(s1, s2); + + // CHECK: [[S1:%.+]] = load i16, ptr %s1.addr, align 2 + // CHECK: [[I1:%.+]] = sext i16 [[S1]] to i32 + // CHECK-NEXT: [[S2:%.+]] = load i16, ptr %s2.addr, align 2 + // CHECK: [[I2:%.+]] = sext i16 [[S2]] to i32 + // CHECK-NEXT: call i32 @llvm.sadd.sat.i32(i32 [[I1]], i32 [[I2]]) + s1 = __builtin_elementwise_add_sat((int)s1, (int)s2); + + // CHECK: [[US1:%.+]] = load i16, ptr %us1.addr, align 2 + // CHECK-NEXT: [[US2:%.+]] = load i16, ptr %us2.addr, align 2 + // CHECK-NEXT: call i16 @llvm.uadd.sat.i16(i16 [[US1]], i16 [[US2]]) + us1 = __builtin_elementwise_add_sat(us1, us2); } void test_builtin_elementwise_sub_sat(float f1, float f2, double d1, double d2, @@ -121,7 +191,10 @@ void test_builtin_elementwise_sub_sat(float f1, float f2, double d1, double d2, long long int i2, si8 vi1, si8 vi2, unsigned u1, unsigned u2, u4 vu1, u4 vu2, _BitInt(31) bi1, _BitInt(31) bi2, - unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2) { + unsigned _BitInt(55) bu1, unsigned _BitInt(55) bu2, + char c1, char c2, unsigned char uc1, + unsigned char uc2, short s1, short s2, + unsigned short us1, unsigned short us2) { // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8 // CHECK-NEXT: call i64 @llvm.ssub.sat.i64(i64 [[I1]], i64 [[I2]]) @@ -129,7 +202,7 @@ void test_builtin_elementwise_sub_sat(float f1, float f2, double d1, double d2, // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 // CHECK-NEXT: call i64 @llvm.ssub.sat.i64(i64 [[I1]], i64 10) - i1 = __builtin_elementwise_sub_sat(i1, 10); + i1 = __builtin_elementwise_sub_sat(i1, 10ll); // CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16 // CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, ptr %vi2.addr, align 16 @@ -167,6 +240,26 @@ void test_builtin_elementwise_sub_sat(float f1, float f2, double d1, double d2, // CHECK: call i32 @llvm.ssub.sat.i32(i32 1, i32 97) i1 = __builtin_elementwise_sub_sat(1, 'a'); + + // CHECK: [[C1:%.+]] = load i8, ptr %c1.addr, align 1 + // CHECK-NEXT: [[C2:%.+]] = load i8, ptr %c2.addr, align 1 + // CHECK-NEXT: call i8 @llvm.ssub.sat.i8(i8 [[C1]], i8 [[C2]]) + c1 = __builtin_elementwise_sub_sat(c1, c2); + + // CHECK: [[UC1:%.+]] = load i8, ptr %uc1.addr, align 1 + // CHECK-NEXT: [[UC2:%.+]] = load i8, ptr %uc2.addr, align 1 + // CHECK-NEXT: call i8 @llvm.usub.sat.i8(i8 [[UC1]], i8 [[UC2]]) + uc1 = __builtin_elementwise_sub_sat(uc1, uc2); + + // CHECK: [[S1:%.+]] = load i16, ptr %s1.addr, align 2 + // CHECK-NEXT: [[S2:%.+]] = load i16, ptr %s2.addr, align 2 + // CHECK-NEXT: call i16 @llvm.ssub.sat.i16(i16 [[S1]], i16 [[S2]]) + s1 = __builtin_elementwise_sub_sat(s1, s2); + + // CHECK: [[US1:%.+]] = load i16, ptr %us1.addr, align 2 + // CHECK-NEXT: [[US2:%.+]] = load i16, ptr %us2.addr, align 2 + // CHECK-NEXT: call i16 @llvm.usub.sat.i16(i16 [[US1]], i16 [[US2]]) + us1 = __builtin_elementwise_sub_sat(us1, us2); } void test_builtin_elementwise_max(float f1, float f2, double d1, double d2, @@ -202,7 +295,7 @@ void test_builtin_elementwise_max(float f1, float f2, double d1, double d2, // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 // CHECK-NEXT: call i64 @llvm.smax.i64(i64 [[I1]], i64 10) - i1 = __builtin_elementwise_max(i1, 10); + i1 = __builtin_elementwise_max(i1, 10ll); // CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16 // CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, ptr %vi2.addr, align 16 @@ -286,7 +379,14 @@ void test_builtin_elementwise_min(float f1, float f2, double d1, double d2, // CHECK: [[I2:%.+]] = load i64, ptr %i2.addr, align 8 // CHECK-NEXT: call i64 @llvm.smin.i64(i64 -11, i64 [[I2]]) - i1 = __builtin_elementwise_min(-11, i2); + i1 = __builtin_elementwise_min(-11ll, i2); + + // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 + // CHECK: [[S1:%.+]] = trunc i64 [[I1]] to i16 + // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8 + // CHECK: [[S2:%.+]] = trunc i64 [[I2]] to i16 + // CHECK-NEXT: call i16 @llvm.smin.i16(i16 [[S1]], i16 [[S2]]) + i1 = __builtin_elementwise_min((short)i1, (short)i2); // CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16 // CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, ptr %vi2.addr, align 16 @@ -298,12 +398,6 @@ void test_builtin_elementwise_min(float f1, float f2, double d1, double d2, // CHECK-NEXT: call i32 @llvm.umin.i32(i32 [[U1]], i32 [[U2]]) u1 = __builtin_elementwise_min(u1, u2); - // CHECK: [[U1:%.+]] = load i32, ptr %u1.addr, align 4 - // CHECK-NEXT: [[ZEXT_U1:%.+]] = zext i32 [[U1]] to i64 - // CHECK-NEXT: [[I2:%.+]] = load i64, ptr %i2.addr, align 8 - // CHECK-NEXT: call i64 @llvm.smin.i64(i64 [[ZEXT_U1]], i64 [[I2]]) - u1 = __builtin_elementwise_min(u1, i2); - // CHECK: [[VU1:%.+]] = load <4 x i32>, ptr %vu1.addr, align 16 // CHECK-NEXT: [[VU2:%.+]] = load <4 x i32>, ptr %vu2.addr, align 16 // CHECK-NEXT: call <4 x i32> @llvm.umin.v4i32(<4 x i32> [[VU1]], <4 x i32> [[VU2]]) @@ -342,7 +436,8 @@ void test_builtin_elementwise_min(float f1, float f2, double d1, double d2, void test_builtin_elementwise_bitreverse(si8 vi1, si8 vi2, long long int i1, long long int i2, short si, - _BitInt(31) bi1, _BitInt(31) bi2) { + _BitInt(31) bi1, _BitInt(31) bi2, + char ci) { // CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8 @@ -371,9 +466,7 @@ void test_builtin_elementwise_bitreverse(si8 vi1, si8 vi2, b = __builtin_elementwise_bitreverse(-10); // CHECK: [[SI:%.+]] = load i16, ptr %si.addr, align 2 - // CHECK-NEXT: [[SI_EXT:%.+]] = sext i16 [[SI]] to i32 - // CHECK-NEXT: [[RES:%.+]] = call i32 @llvm.bitreverse.i32(i32 [[SI_EXT]]) - // CHECK-NEXT: = trunc i32 [[RES]] to i16 + // CHECK-NEXT: [[RES:%.+]] = call i16 @llvm.bitreverse.i16(i16 [[SI]]) si = __builtin_elementwise_bitreverse(si); } diff --git a/clang/test/CodeGenHLSL/builtins/dot-builtin.hlsl b/clang/test/CodeGenHLSL/builtins/dot-builtin.hlsl index b0b95074c972d..9e9c331d6698b 100644 --- a/clang/test/CodeGenHLSL/builtins/dot-builtin.hlsl +++ b/clang/test/CodeGenHLSL/builtins/dot-builtin.hlsl @@ -6,7 +6,7 @@ // CHECK: %conv2 = fptrunc double %dx.dot to float // CHECK: ret float %conv2 float builtin_bool_to_float_type_promotion ( float p0, bool p1 ) { - return __builtin_hlsl_dot ( p0, p1 ); + return __builtin_hlsl_dot ( (double)p0, (double)p1 ); } // CHECK-LABEL: builtin_bool_to_float_arg1_type_promotion @@ -16,7 +16,7 @@ float builtin_bool_to_float_type_promotion ( float p0, bool p1 ) { // CHECK: %conv2 = fptrunc double %dx.dot to float // CHECK: ret float %conv2 float builtin_bool_to_float_arg1_type_promotion ( bool p0, float p1 ) { - return __builtin_hlsl_dot ( p0, p1 ); + return __builtin_hlsl_dot ( (double)p0, (double)p1 ); } // CHECK-LABEL: builtin_dot_int_to_float_promotion @@ -26,5 +26,5 @@ float builtin_bool_to_float_arg1_type_promotion ( bool p0, float p1 ) { // CHECK: %conv2 = fptrunc double %dx.dot to float // CHECK: ret float %conv2 float builtin_dot_int_to_float_promotion ( float p0, int p1 ) { - return __builtin_hlsl_dot ( p0, p1 ); + return __builtin_hlsl_dot ( (double)p0, (double)p1 ); } diff --git a/clang/test/Sema/aarch64-sve-vector-pow-ops.c b/clang/test/Sema/aarch64-sve-vector-pow-ops.c index e61d752911fc3..16dbe33c1afe7 100644 --- a/clang/test/Sema/aarch64-sve-vector-pow-ops.c +++ b/clang/test/Sema/aarch64-sve-vector-pow-ops.c @@ -7,5 +7,5 @@ svfloat32_t test_pow_vv_i8mf8(svfloat32_t v) { return __builtin_elementwise_pow(v, v); - // expected-error@-1 {{1st argument must be a vector, integer or floating point type}} + // expected-error@-1 {{1st argument must be a floating point type}} } diff --git a/clang/test/Sema/builtins-elementwise-math.c b/clang/test/Sema/builtins-elementwise-math.c index 2673f1f519af6..fff892c4b05ff 100644 --- a/clang/test/Sema/builtins-elementwise-math.c +++ b/clang/test/Sema/builtins-elementwise-math.c @@ -69,14 +69,18 @@ void test_builtin_elementwise_add_sat(int i, short s, double d, float4 v, int3 i // expected-error@-1 {{1st argument must be a vector of integers (was 'float4' (vector of 4 'float' values))}} s = __builtin_elementwise_add_sat(i, s); + // expected-error@-1 {{arguments are of different types ('int' vs 'short')}} enum e { one, two }; i = __builtin_elementwise_add_sat(one, two); + i = __builtin_elementwise_add_sat(one, d); + // expected-error@-1 {{arguments are of different types ('int' vs 'double')}} + enum f { three }; enum f x = __builtin_elementwise_add_sat(one, three); - // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}} + // expected-error@-1 {{invalid arithmetic between different enumeration types ('enum e' and 'enum f')}} _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_add_sat(ext, ext); @@ -128,14 +132,18 @@ void test_builtin_elementwise_sub_sat(int i, short s, double d, float4 v, int3 i // expected-error@-1 {{1st argument must be a vector of integers (was 'float4' (vector of 4 'float' values))}} s = __builtin_elementwise_sub_sat(i, s); + // expected-error@-1 {{arguments are of different types ('int' vs 'short')}} enum e { one, two }; i = __builtin_elementwise_sub_sat(one, two); + i = __builtin_elementwise_sub_sat(one, d); + // expected-error@-1 {{arguments are of different types ('int' vs 'double')}} + enum f { three }; enum f x = __builtin_elementwise_sub_sat(one, three); - // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}} + // expected-error@-1 {{invalid arithmetic between different enumeration types ('enum e' and 'enum f')}} _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_sub_sat(ext, ext); @@ -184,14 +192,18 @@ void test_builtin_elementwise_max(int i, short s, double d, float4 v, int3 iv, u // expected-error@-1 {{arguments are of different types ('unsigned3' (vector of 3 'unsigned int' values) vs 'int3' (vector of 3 'int' values))}} s = __builtin_elementwise_max(i, s); + // expected-error@-1 {{arguments are of different types ('int' vs 'short')}} enum e { one, two }; i = __builtin_elementwise_max(one, two); + i = __builtin_elementwise_max(one, d); + // expected-error@-1 {{arguments are of different types ('int' vs 'double')}} + enum f { three }; enum f x = __builtin_elementwise_max(one, three); - // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}} + // expected-error@-1 {{invalid arithmetic between different enumeration types ('enum e' and 'enum f')}} _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_max(ext, ext); @@ -240,14 +252,18 @@ void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, u // expected-error@-1 {{arguments are of different types ('unsigned3' (vector of 3 'unsigned int' values) vs 'int3' (vector of 3 'int' values))}} s = __builtin_elementwise_min(i, s); + // expected-error@-1 {{arguments are of different types ('int' vs 'short')}} enum e { one, two }; i = __builtin_elementwise_min(one, two); + i = __builtin_elementwise_min(one, d); + // expected-error@-1 {{arguments are of different types ('int' vs 'double')}} + enum f { three }; enum f x = __builtin_elementwise_min(one, three); - // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}} + // expected-error@-1 {{invalid arithmetic between different enumeration types ('enum e' and 'enum f')}} _BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}} ext = __builtin_elementwise_min(ext, ext); @@ -273,6 +289,7 @@ void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, u // expected-error@-1 {{1st argument must be a vector, integer or floating point type (was '_Complex float')}} } + void test_builtin_elementwise_bitreverse(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) { struct Foo s = __builtin_elementwise_ceil(f); diff --git a/clang/test/Sema/riscv-sve-vector-pow-ops.c b/clang/test/Sema/riscv-sve-vector-pow-ops.c index 78efb4b549f80..524e453441780 100644 --- a/clang/test/Sema/riscv-sve-vector-pow-ops.c +++ b/clang/test/Sema/riscv-sve-vector-pow-ops.c @@ -9,5 +9,5 @@ vfloat32mf2_t test_pow_vv_i8mf8(vfloat32mf2_t v) { return __builtin_elementwise_pow(v, v); - // expected-error@-1 {{1st argument must be a vector, integer or floating point type}} + // expected-error@-1 {{1st argument must be a floating point type}} } diff --git a/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl index ee4605528f410..b96761fb76cc0 100644 --- a/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl @@ -84,3 +84,10 @@ float builtin_mad_int_to_float_promotion(float p0, int p1) { return __builtin_hlsl_mad(p0, p0, p1); // expected-error@-1 {{3rd argument must be a floating point type (was 'int')}} } + +int builtin_mad_mixed_enums() { + enum e { one, two }; + enum f { three }; + return __builtin_hlsl_mad(one, two, three); + // expected-error@-1 {{invalid arithmetic between different enumeration types ('e' and 'f')}} +}