From b7e4260acb1c9a30249c0660089ea3185065ee45 Mon Sep 17 00:00:00 2001 From: Fraser Cormack Date: Wed, 29 Jan 2025 09:40:04 +0000 Subject: [PATCH] [clang] Restrict the use of scalar types in vector builtins (#119423) This commit restricts the use of scalar types in vector math builtins, particularly the `__builtin_elementwise_*` builtins. Previously, small scalar integer types would be promoted to `int`, as per the usual conversions. This would silently do the wrong thing for certain operations, such as `add_sat`, `popcount`, `bitreverse`, and others. Similarly, since unsigned integer types were promoted to `int`, something like `add_sat(unsigned char, unsigned char)` would perform a *signed* operation. With this patch, promotable scalar integer types are not promoted to int, and are kept intact. If any of the types differ in the binary and ternary builtins, an error is issued. Similarly an error is issued if builtins are supplied integer types of different signs. Mixing enums of different types in binary/ternary builtins now consistently raises an error in all language modes. This brings the behaviour surrounding scalar types more in line with that of vector types. No change is made to vector types, which are both not promoted and whose element types must match. Fixes #84047. RFC: https://discourse.llvm.org/t/rfc-change-behaviour-of-elementwise-builtins-on-scalar-integer-types/83725 --- clang/docs/LanguageExtensions.rst | 65 ++++----- clang/include/clang/Sema/Sema.h | 23 ++- clang/lib/Sema/SemaChecking.cpp | 117 ++++++++++----- clang/lib/Sema/SemaExpr.cpp | 83 ++++++----- .../test/CodeGen/builtins-elementwise-math.c | 133 +++++++++++++++--- .../CodeGenHLSL/builtins/dot-builtin.hlsl | 6 +- clang/test/Sema/aarch64-sve-vector-pow-ops.c | 2 +- clang/test/Sema/builtins-elementwise-math.c | 25 +++- clang/test/Sema/riscv-sve-vector-pow-ops.c | 2 +- clang/test/SemaHLSL/BuiltIns/mad-errors.hlsl | 7 + 10 files changed, 322 insertions(+), 141 deletions(-) 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')}} +}