Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[clang] Restrict the use of scalar types in vector builtins #119423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@ The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``
``__builtin_elementwise_bitreverse``, ``__builtin_elementwise_add_sat``,
``__builtin_elementwise_sub_sat`` can be called in a ``constexpr`` context.

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
============================================== ====================================================================== =========================================
Expand Down
17 changes: 14 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2323,7 +2323,8 @@ class Sema final : public SemaBase {
const FunctionProtoType *Proto);

/// \param FPOnly restricts the arguments to floating-point types.
bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false);
std::optional<QualType> BuiltinVectorMath(CallExpr *TheCall,
bool FPOnly = false);
bool BuiltinVectorToScalarMath(CallExpr *TheCall);

void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction,
Expand Down Expand Up @@ -7491,10 +7492,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);
Expand Down Expand Up @@ -7557,6 +7563,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
Expand Down
106 changes: 73 additions & 33 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14594,11 +14594,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;

Expand All @@ -14613,67 +14625,95 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
}

bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) {
QualType Res;
if (BuiltinVectorMath(TheCall, Res, FPOnly))
return true;
TheCall->setType(Res);
return false;
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<QualType> Res = BuiltinVectorMath(TheCall);
if (!Res)
return true;

if (auto *VecTy0 = Res->getAs<VectorType>())
if (auto *VecTy0 = (*Res)->getAs<VectorType>())
TheCall->setType(VecTy0->getElementType());
else
TheCall->setType(Res);
TheCall->setType(*Res);

return false;
}

bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly) {
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<QualType> 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 (TyA.getCanonicalType() != TyB.getCanonicalType()) {
Diag(LocA, diag::err_typecheck_call_different_arg_types) << TyA << TyB;
return std::nullopt;
}

if (FPOnly) {
if (checkFPMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
return true;
if (checkFPMathBuiltinElementType(*this, LocA, TyA, 1))
return std::nullopt;
} else {
if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
return true;
if (checkMathBuiltinElementType(*this, LocA, TyA, 1))
return std::nullopt;
}

TheCall->setArg(0, A.get());
TheCall->setArg(1, B.get());
return false;
TheCall->setArg(0, Args[0]);
TheCall->setArg(1, Args[1]);
return TyA;
}

bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall,
bool CheckForFloatArgs) {
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();
Expand Down
83 changes: 48 additions & 35 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,20 +776,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() &&
Expand Down Expand Up @@ -827,7 +818,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.
Expand Down Expand Up @@ -1489,64 +1503,63 @@ 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
// deprecated ([depr.arith.conv.enum]).
//
// 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<EnumType>()->getDecl()->hasNameForLinkage() ||
!R->castAs<EnumType>()->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;
}
}

Expand All @@ -1557,7 +1570,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());
Expand Down
Loading
Loading