diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3308,10 +3308,11 @@ "alignment assumed">, InGroup>; def warn_not_xl_compatible - : Warning<"requesting an alignment of 16 bytes or greater for struct" - " members is not binary compatible with IBM XL C/C++ for AIX" - " 16.1.0 and older">, + : Warning<"alignment of 16 bytes for a struct member is not binary " + "compatible with IBM XL C/C++ for AIX 16.1.0 or older">, InGroup; +def note_misaligned_member_used_here : Note< + "passing byval argument %0 with potentially incompatible alignment here">; def warn_redeclaration_without_attribute_prev_attribute_ignored : Warning< "%q0 redeclared without %1 attribute: previous %1 ignored">, InGroup; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12739,6 +12739,9 @@ ArrayRef Args, const FunctionProtoType *Proto, SourceLocation Loc); + void checkAIXMemberAlignment(SourceLocation Loc, NamedDecl *FDecl, + const Expr *Arg); + void CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, StringRef ParamName, QualType ArgTy, QualType ParamTy); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -5235,10 +5235,49 @@ } } -/// Warn if a pointer or reference argument passed to a function points to an -/// object that is less aligned than the parameter. This can happen when -/// creating a typedef with a lower alignment than the original type and then -/// calling functions defined in terms of the original type. +// 16 byte ByVal alignment not due to a vector member is not honoured by XL +// on AIX. Emit a warning here that users are generating binary incompatible +// code to be safe. +// Here we try to get information about the alignment of the struct member +// from the struct passed to the caller function. We only warn when the struct +// is passed byval, hence the series of checks and early returns if we are a not +// passing a struct byval. +void Sema::checkAIXMemberAlignment(SourceLocation Loc, NamedDecl *FDecl, + const Expr *Arg) { + + if (!isa(Arg->IgnoreParens())) + return; + if (!isa( + (dyn_cast(Arg->IgnoreParens()))->getSubExpr())) + return; + + const auto *PVD = dyn_cast( + (dyn_cast( + (dyn_cast(Arg->IgnoreParens()))->getSubExpr())) + ->getDecl()); + if (!PVD) + return; + if (!PVD->getType()->isRecordType()) + return; + + QualType ArgType = Arg->getType(); + for (const FieldDecl *FD : + ArgType->castAs()->getDecl()->fields()) { + if (const auto *AA = FD->getAttr()) { + CharUnits Alignment = + Context.toCharUnitsFromBits(AA->getAlignment(Context)); + if (Alignment.getQuantity() == 16) { + Diag(FD->getLocation(), diag::warn_not_xl_compatible) << FD; + Diag(Loc, diag::note_misaligned_member_used_here) << PVD; + } + } + } +} + +/// Warn if a pointer or reference argument passed to a function points to +/// an object that is less aligned than the parameter. This can happen when +/// creating a typedef with a lower alignment than the original type and +/// then calling functions defined in terms of the original type. void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, StringRef ParamName, QualType ArgTy, QualType ParamTy) { @@ -5272,908 +5311,932 @@ Diag(Loc, diag::warn_param_mismatched_alignment) << (int)ArgAlign.getQuantity() << (int)ParamAlign.getQuantity() << ParamName << (FDecl != nullptr) << FDecl; -} + } -/// Handles the checks for format strings, non-POD arguments to vararg -/// functions, NULL arguments passed to non-NULL parameters, and diagnose_if -/// attributes. -void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, - const Expr *ThisArg, ArrayRef Args, - bool IsMemberFunction, SourceLocation Loc, - SourceRange Range, VariadicCallType CallType) { - // FIXME: We should check as much as we can in the template definition. - if (CurContext->isDependentContext()) - return; + /// Handles the checks for format strings, non-POD arguments to vararg + /// functions, NULL arguments passed to non-NULL parameters, and diagnose_if + /// attributes. + void Sema::checkCall(NamedDecl * FDecl, const FunctionProtoType *Proto, + const Expr *ThisArg, ArrayRef Args, + bool IsMemberFunction, SourceLocation Loc, + SourceRange Range, VariadicCallType CallType) { + // FIXME: We should check as much as we can in the template definition. + if (CurContext->isDependentContext()) + return; - // Printf and scanf checking. - llvm::SmallBitVector CheckedVarArgs; - if (FDecl) { - for (const auto *I : FDecl->specific_attrs()) { - // Only create vector if there are format attributes. - CheckedVarArgs.resize(Args.size()); - - CheckFormatArguments(I, Args, IsMemberFunction, CallType, Loc, Range, - CheckedVarArgs); - } - } - - // Refuse POD arguments that weren't caught by the format string - // checks above. - auto *FD = dyn_cast_or_null(FDecl); - if (CallType != VariadicDoesNotApply && - (!FD || FD->getBuiltinID() != Builtin::BI__noop)) { - unsigned NumParams = Proto ? Proto->getNumParams() - : FDecl && isa(FDecl) - ? cast(FDecl)->getNumParams() - : FDecl && isa(FDecl) - ? cast(FDecl)->param_size() - : 0; - - for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) { - // Args[ArgIdx] can be null in malformed code. - if (const Expr *Arg = Args[ArgIdx]) { - if (CheckedVarArgs.empty() || !CheckedVarArgs[ArgIdx]) - checkVariadicArgument(Arg, CallType); - } - } - } + // Printf and scanf checking. + llvm::SmallBitVector CheckedVarArgs; + if (FDecl) { + for (const auto *I : FDecl->specific_attrs()) { + // Only create vector if there are format attributes. + CheckedVarArgs.resize(Args.size()); - if (FDecl || Proto) { - CheckNonNullArguments(*this, FDecl, Proto, Args, Loc); + CheckFormatArguments(I, Args, IsMemberFunction, CallType, Loc, Range, + CheckedVarArgs); + } + } - // Type safety checking. - if (FDecl) { - for (const auto *I : FDecl->specific_attrs()) - CheckArgumentWithTypeTag(I, Args, Loc); - } - } + // Refuse POD arguments that weren't caught by the format string + // checks above. + auto *FD = dyn_cast_or_null(FDecl); + if (CallType != VariadicDoesNotApply && + (!FD || FD->getBuiltinID() != Builtin::BI__noop)) { + unsigned NumParams = Proto ? Proto->getNumParams() + : FDecl && isa(FDecl) + ? cast(FDecl)->getNumParams() + : FDecl && isa(FDecl) + ? cast(FDecl)->param_size() + : 0; + + for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) { + // Args[ArgIdx] can be null in malformed code. + if (const Expr *Arg = Args[ArgIdx]) { + if (CheckedVarArgs.empty() || !CheckedVarArgs[ArgIdx]) + checkVariadicArgument(Arg, CallType); + } + } + } - // Check that passed arguments match the alignment of original arguments. - // Try to get the missing prototype from the declaration. - if (!Proto && FDecl) { - const auto *FT = FDecl->getFunctionType(); - if (isa_and_nonnull(FT)) - Proto = cast(FDecl->getFunctionType()); - } - if (Proto) { - // For variadic functions, we may have more args than parameters. - // For some K&R functions, we may have less args than parameters. - const auto N = std::min(Proto->getNumParams(), Args.size()); - for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) { - // Args[ArgIdx] can be null in malformed code. - if (const Expr *Arg = Args[ArgIdx]) { - if (Arg->containsErrors()) - continue; + if (FDecl || Proto) { + CheckNonNullArguments(*this, FDecl, Proto, Args, Loc); - QualType ParamTy = Proto->getParamType(ArgIdx); - QualType ArgTy = Arg->getType(); - CheckArgAlignment(Arg->getExprLoc(), FDecl, std::to_string(ArgIdx + 1), - ArgTy, ParamTy); + // Type safety checking. + if (FDecl) { + for (const auto *I : FDecl->specific_attrs()) + CheckArgumentWithTypeTag(I, Args, Loc); + } } - } - } - if (FDecl && FDecl->hasAttr()) { - auto *AA = FDecl->getAttr(); - const Expr *Arg = Args[AA->getParamIndex().getASTIndex()]; - if (!Arg->isValueDependent()) { - Expr::EvalResult Align; - if (Arg->EvaluateAsInt(Align, Context)) { - const llvm::APSInt &I = Align.Val.getInt(); - if (!I.isPowerOf2()) - Diag(Arg->getExprLoc(), diag::warn_alignment_not_power_of_two) - << Arg->getSourceRange(); + // Check that passed arguments match the alignment of original arguments. + // Try to get the missing prototype from the declaration. + if (!Proto && FDecl) { + const auto *FT = FDecl->getFunctionType(); + if (isa_and_nonnull(FT)) + Proto = cast(FDecl->getFunctionType()); + } + if (Proto) { + // For variadic functions, we may have more args than parameters. + // For some K&R functions, we may have less args than parameters. + const auto N = std::min(Proto->getNumParams(), Args.size()); + for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) { + // Args[ArgIdx] can be null in malformed code. + if (const Expr *Arg = Args[ArgIdx]) { + if (Arg->containsErrors()) + continue; + + QualType ParamTy = Proto->getParamType(ArgIdx); + QualType ArgTy = Arg->getType(); + if (Context.getTargetInfo().getTriple().isOSAIX() && Arg && + FDecl->hasLinkage() && + FDecl->getFormalLinkage() != InternalLinkage && + CallType == VariadicDoesNotApply) + checkAIXMemberAlignment((Arg->getExprLoc()), FDecl, Arg); + + CheckArgAlignment(Arg->getExprLoc(), FDecl, + std::to_string(ArgIdx + 1), ArgTy, ParamTy); + } + } + } - if (I > Sema::MaximumAlignment) - Diag(Arg->getExprLoc(), diag::warn_assume_aligned_too_great) - << Arg->getSourceRange() << Sema::MaximumAlignment; + if (FDecl && FDecl->hasAttr()) { + auto *AA = FDecl->getAttr(); + const Expr *Arg = Args[AA->getParamIndex().getASTIndex()]; + if (!Arg->isValueDependent()) { + Expr::EvalResult Align; + if (Arg->EvaluateAsInt(Align, Context)) { + const llvm::APSInt &I = Align.Val.getInt(); + if (!I.isPowerOf2()) + Diag(Arg->getExprLoc(), diag::warn_alignment_not_power_of_two) + << Arg->getSourceRange(); + + if (I > Sema::MaximumAlignment) + Diag(Arg->getExprLoc(), diag::warn_assume_aligned_too_great) + << Arg->getSourceRange() << Sema::MaximumAlignment; + } + } } - } - } - if (FD) - diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc); -} + if (FD) + diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc); + } + + /// CheckConstructorCall - Check a constructor call for correctness and + /// safety properties not enforced by the C type system. + void Sema::CheckConstructorCall( + FunctionDecl * FDecl, QualType ThisType, ArrayRef Args, + const FunctionProtoType *Proto, SourceLocation Loc) { + VariadicCallType CallType = + Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; + + auto *Ctor = cast(FDecl); + CheckArgAlignment(Loc, FDecl, "'this'", Context.getPointerType(ThisType), + Context.getPointerType(Ctor->getThisObjectType())); + + checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, + /*IsMemberFunction=*/true, Loc, SourceRange(), CallType); + } + + /// CheckFunctionCall - Check a direct function call for various correctness + /// and safety properties not strictly enforced by the C type system. + bool Sema::CheckFunctionCall(FunctionDecl * FDecl, CallExpr * TheCall, + const FunctionProtoType *Proto) { + bool IsMemberOperatorCall = + isa(TheCall) && isa(FDecl); + bool IsMemberFunction = + isa(TheCall) || IsMemberOperatorCall; + VariadicCallType CallType = + getVariadicCallType(FDecl, Proto, TheCall->getCallee()); + Expr **Args = TheCall->getArgs(); + unsigned NumArgs = TheCall->getNumArgs(); + + Expr *ImplicitThis = nullptr; + if (IsMemberOperatorCall) { + // If this is a call to a member operator, hide the first argument + // from checkCall. + // FIXME: Our choice of AST representation here is less than ideal. + ImplicitThis = Args[0]; + ++Args; + --NumArgs; + } else if (IsMemberFunction) + ImplicitThis = + cast(TheCall)->getImplicitObjectArgument(); + + if (ImplicitThis) { + // ImplicitThis may or may not be a pointer, depending on whether . or + // -> is used. + QualType ThisType = ImplicitThis->getType(); + if (!ThisType->isPointerType()) { + assert(!ThisType->isReferenceType()); + ThisType = Context.getPointerType(ThisType); + } -/// CheckConstructorCall - Check a constructor call for correctness and safety -/// properties not enforced by the C type system. -void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType, - ArrayRef Args, - const FunctionProtoType *Proto, - SourceLocation Loc) { - VariadicCallType CallType = - Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; - - auto *Ctor = cast(FDecl); - CheckArgAlignment(Loc, FDecl, "'this'", Context.getPointerType(ThisType), - Context.getPointerType(Ctor->getThisObjectType())); - - checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true, - Loc, SourceRange(), CallType); -} - -/// CheckFunctionCall - Check a direct function call for various correctness -/// and safety properties not strictly enforced by the C type system. -bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, - const FunctionProtoType *Proto) { - bool IsMemberOperatorCall = isa(TheCall) && - isa(FDecl); - bool IsMemberFunction = isa(TheCall) || - IsMemberOperatorCall; - VariadicCallType CallType = getVariadicCallType(FDecl, Proto, - TheCall->getCallee()); - Expr** Args = TheCall->getArgs(); - unsigned NumArgs = TheCall->getNumArgs(); + QualType ThisTypeFromDecl = Context.getPointerType( + cast(FDecl)->getThisObjectType()); - Expr *ImplicitThis = nullptr; - if (IsMemberOperatorCall) { - // If this is a call to a member operator, hide the first argument - // from checkCall. - // FIXME: Our choice of AST representation here is less than ideal. - ImplicitThis = Args[0]; - ++Args; - --NumArgs; - } else if (IsMemberFunction) - ImplicitThis = - cast(TheCall)->getImplicitObjectArgument(); - - if (ImplicitThis) { - // ImplicitThis may or may not be a pointer, depending on whether . or -> is - // used. - QualType ThisType = ImplicitThis->getType(); - if (!ThisType->isPointerType()) { - assert(!ThisType->isReferenceType()); - ThisType = Context.getPointerType(ThisType); - } - - QualType ThisTypeFromDecl = - Context.getPointerType(cast(FDecl)->getThisObjectType()); - - CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType, - ThisTypeFromDecl); - } - - checkCall(FDecl, Proto, ImplicitThis, llvm::makeArrayRef(Args, NumArgs), - IsMemberFunction, TheCall->getRParenLoc(), - TheCall->getCallee()->getSourceRange(), CallType); - - IdentifierInfo *FnInfo = FDecl->getIdentifier(); - // None of the checks below are needed for functions that don't have - // simple names (e.g., C++ conversion functions). - if (!FnInfo) - return false; + CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType, + ThisTypeFromDecl); + } - CheckTCBEnforcement(TheCall, FDecl); + checkCall(FDecl, Proto, ImplicitThis, llvm::makeArrayRef(Args, NumArgs), + IsMemberFunction, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); - CheckAbsoluteValueFunction(TheCall, FDecl); - CheckMaxUnsignedZero(TheCall, FDecl); + IdentifierInfo *FnInfo = FDecl->getIdentifier(); + // None of the checks below are needed for functions that don't have + // simple names (e.g., C++ conversion functions). + if (!FnInfo) + return false; - if (getLangOpts().ObjC) - DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs); + CheckTCBEnforcement(TheCall, FDecl); - unsigned CMId = FDecl->getMemoryFunctionKind(); + CheckAbsoluteValueFunction(TheCall, FDecl); + CheckMaxUnsignedZero(TheCall, FDecl); - // Handle memory setting and copying functions. - switch (CMId) { - case 0: - return false; - case Builtin::BIstrlcpy: // fallthrough - case Builtin::BIstrlcat: - CheckStrlcpycatArguments(TheCall, FnInfo); - break; - case Builtin::BIstrncat: - CheckStrncatArguments(TheCall, FnInfo); - break; - case Builtin::BIfree: - CheckFreeArguments(TheCall); - break; - default: - CheckMemaccessArguments(TheCall, CMId, FnInfo); - } + if (getLangOpts().ObjC) + DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs); - return false; -} + unsigned CMId = FDecl->getMemoryFunctionKind(); -bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, - ArrayRef Args) { - VariadicCallType CallType = - Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; + // Handle memory setting and copying functions. + switch (CMId) { + case 0: + return false; + case Builtin::BIstrlcpy: // fallthrough + case Builtin::BIstrlcat: + CheckStrlcpycatArguments(TheCall, FnInfo); + break; + case Builtin::BIstrncat: + CheckStrncatArguments(TheCall, FnInfo); + break; + case Builtin::BIfree: + CheckFreeArguments(TheCall); + break; + default: + CheckMemaccessArguments(TheCall, CMId, FnInfo); + } - checkCall(Method, nullptr, /*ThisArg=*/nullptr, Args, - /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), - CallType); + return false; + } - return false; -} + bool Sema::CheckObjCMethodCall(ObjCMethodDecl * Method, + SourceLocation lbrac, + ArrayRef Args) { + VariadicCallType CallType = + Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; -bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall, - const FunctionProtoType *Proto) { - QualType Ty; - if (const auto *V = dyn_cast(NDecl)) - Ty = V->getType().getNonReferenceType(); - else if (const auto *F = dyn_cast(NDecl)) - Ty = F->getType().getNonReferenceType(); - else - return false; + checkCall(Method, nullptr, /*ThisArg=*/nullptr, Args, + /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), + CallType); - if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() && - !Ty->isFunctionProtoType()) - return false; + return false; + } - VariadicCallType CallType; - if (!Proto || !Proto->isVariadic()) { - CallType = VariadicDoesNotApply; - } else if (Ty->isBlockPointerType()) { - CallType = VariadicBlock; - } else { // Ty->isFunctionPointerType() - CallType = VariadicFunction; - } + bool Sema::CheckPointerCall(NamedDecl * NDecl, CallExpr * TheCall, + const FunctionProtoType *Proto) { + QualType Ty; + if (const auto *V = dyn_cast(NDecl)) + Ty = V->getType().getNonReferenceType(); + else if (const auto *F = dyn_cast(NDecl)) + Ty = F->getType().getNonReferenceType(); + else + return false; - checkCall(NDecl, Proto, /*ThisArg=*/nullptr, - llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), - /*IsMemberFunction=*/false, TheCall->getRParenLoc(), - TheCall->getCallee()->getSourceRange(), CallType); + if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() && + !Ty->isFunctionProtoType()) + return false; - return false; -} + VariadicCallType CallType; + if (!Proto || !Proto->isVariadic()) { + CallType = VariadicDoesNotApply; + } else if (Ty->isBlockPointerType()) { + CallType = VariadicBlock; + } else { // Ty->isFunctionPointerType() + CallType = VariadicFunction; + } -/// Checks function calls when a FunctionDecl or a NamedDecl is not available, -/// such as function pointers returned from functions. -bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) { - VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto, - TheCall->getCallee()); - checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr, - llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), - /*IsMemberFunction=*/false, TheCall->getRParenLoc(), - TheCall->getCallee()->getSourceRange(), CallType); + checkCall(NDecl, Proto, /*ThisArg=*/nullptr, + llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), + /*IsMemberFunction=*/false, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); - return false; -} + return false; + } -static bool isValidOrderingForOp(int64_t Ordering, AtomicExpr::AtomicOp Op) { - if (!llvm::isValidAtomicOrderingCABI(Ordering)) - return false; + /// Checks function calls when a FunctionDecl or a NamedDecl is not + /// available, such as function pointers returned from functions. + bool Sema::CheckOtherCall(CallExpr * TheCall, + const FunctionProtoType *Proto) { + VariadicCallType CallType = + getVariadicCallType(/*FDecl=*/nullptr, Proto, TheCall->getCallee()); + checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr, + llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), + /*IsMemberFunction=*/false, TheCall->getRParenLoc(), + TheCall->getCallee()->getSourceRange(), CallType); - auto OrderingCABI = (llvm::AtomicOrderingCABI)Ordering; - switch (Op) { - case AtomicExpr::AO__c11_atomic_init: - case AtomicExpr::AO__opencl_atomic_init: - llvm_unreachable("There is no ordering argument for an init"); - - case AtomicExpr::AO__c11_atomic_load: - case AtomicExpr::AO__opencl_atomic_load: - case AtomicExpr::AO__hip_atomic_load: - case AtomicExpr::AO__atomic_load_n: - case AtomicExpr::AO__atomic_load: - return OrderingCABI != llvm::AtomicOrderingCABI::release && - OrderingCABI != llvm::AtomicOrderingCABI::acq_rel; - - case AtomicExpr::AO__c11_atomic_store: - case AtomicExpr::AO__opencl_atomic_store: - case AtomicExpr::AO__hip_atomic_store: - case AtomicExpr::AO__atomic_store: - case AtomicExpr::AO__atomic_store_n: - return OrderingCABI != llvm::AtomicOrderingCABI::consume && - OrderingCABI != llvm::AtomicOrderingCABI::acquire && - OrderingCABI != llvm::AtomicOrderingCABI::acq_rel; + return false; + } - default: - return true; - } -} + static bool isValidOrderingForOp(int64_t Ordering, + AtomicExpr::AtomicOp Op) { + if (!llvm::isValidAtomicOrderingCABI(Ordering)) + return false; -ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, - AtomicExpr::AtomicOp Op) { - CallExpr *TheCall = cast(TheCallResult.get()); - DeclRefExpr *DRE =cast(TheCall->getCallee()->IgnoreParenCasts()); - MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()}; - return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()}, - DRE->getSourceRange(), TheCall->getRParenLoc(), Args, - Op); -} - -ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, - SourceLocation RParenLoc, MultiExprArg Args, - AtomicExpr::AtomicOp Op, - AtomicArgumentOrder ArgOrder) { - // All the non-OpenCL operations take one of the following forms. - // The OpenCL operations take the __c11 forms with one extra argument for - // synchronization scope. - enum { - // C __c11_atomic_init(A *, C) - Init, - - // C __c11_atomic_load(A *, int) - Load, - - // void __atomic_load(A *, CP, int) - LoadCopy, - - // void __atomic_store(A *, CP, int) - Copy, - - // C __c11_atomic_add(A *, M, int) - Arithmetic, - - // C __atomic_exchange_n(A *, CP, int) - Xchg, - - // void __atomic_exchange(A *, C *, CP, int) - GNUXchg, - - // bool __c11_atomic_compare_exchange_strong(A *, C *, CP, int, int) - C11CmpXchg, - - // bool __atomic_compare_exchange(A *, C *, CP, bool, int, int) - GNUCmpXchg - } Form = Init; - - const unsigned NumForm = GNUCmpXchg + 1; - const unsigned NumArgs[] = { 2, 2, 3, 3, 3, 3, 4, 5, 6 }; - const unsigned NumVals[] = { 1, 0, 1, 1, 1, 1, 2, 2, 3 }; - // where: - // C is an appropriate type, - // A is volatile _Atomic(C) for __c11 builtins and is C for GNU builtins, - // CP is C for __c11 builtins and GNU _n builtins and is C * otherwise, - // M is C if C is an integer, and ptrdiff_t if C is a pointer, and - // the int parameters are for orderings. - - static_assert(sizeof(NumArgs)/sizeof(NumArgs[0]) == NumForm - && sizeof(NumVals)/sizeof(NumVals[0]) == NumForm, - "need to update code for modified forms"); - static_assert(AtomicExpr::AO__c11_atomic_init == 0 && - AtomicExpr::AO__c11_atomic_fetch_min + 1 == - AtomicExpr::AO__atomic_load, - "need to update code for modified C11 atomics"); - bool IsOpenCL = Op >= AtomicExpr::AO__opencl_atomic_init && - Op <= AtomicExpr::AO__opencl_atomic_fetch_max; - bool IsHIP = Op >= AtomicExpr::AO__hip_atomic_load && - Op <= AtomicExpr::AO__hip_atomic_fetch_max; - bool IsC11 = (Op >= AtomicExpr::AO__c11_atomic_init && - Op <= AtomicExpr::AO__c11_atomic_fetch_min) || - IsOpenCL; - bool IsN = Op == AtomicExpr::AO__atomic_load_n || - Op == AtomicExpr::AO__atomic_store_n || - Op == AtomicExpr::AO__atomic_exchange_n || - Op == AtomicExpr::AO__atomic_compare_exchange_n; - bool IsAddSub = false; - - switch (Op) { - case AtomicExpr::AO__c11_atomic_init: - case AtomicExpr::AO__opencl_atomic_init: - Form = Init; - break; + auto OrderingCABI = (llvm::AtomicOrderingCABI)Ordering; + switch (Op) { + case AtomicExpr::AO__c11_atomic_init: + case AtomicExpr::AO__opencl_atomic_init: + llvm_unreachable("There is no ordering argument for an init"); + + case AtomicExpr::AO__c11_atomic_load: + case AtomicExpr::AO__opencl_atomic_load: + case AtomicExpr::AO__hip_atomic_load: + case AtomicExpr::AO__atomic_load_n: + case AtomicExpr::AO__atomic_load: + return OrderingCABI != llvm::AtomicOrderingCABI::release && + OrderingCABI != llvm::AtomicOrderingCABI::acq_rel; + + case AtomicExpr::AO__c11_atomic_store: + case AtomicExpr::AO__opencl_atomic_store: + case AtomicExpr::AO__hip_atomic_store: + case AtomicExpr::AO__atomic_store: + case AtomicExpr::AO__atomic_store_n: + return OrderingCABI != llvm::AtomicOrderingCABI::consume && + OrderingCABI != llvm::AtomicOrderingCABI::acquire && + OrderingCABI != llvm::AtomicOrderingCABI::acq_rel; - case AtomicExpr::AO__c11_atomic_load: - case AtomicExpr::AO__opencl_atomic_load: - case AtomicExpr::AO__hip_atomic_load: - case AtomicExpr::AO__atomic_load_n: - Form = Load; - break; + default: + return true; + } + } - case AtomicExpr::AO__atomic_load: - Form = LoadCopy; - break; + ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult, + AtomicExpr::AtomicOp Op) { + CallExpr *TheCall = cast(TheCallResult.get()); + DeclRefExpr *DRE = + cast(TheCall->getCallee()->IgnoreParenCasts()); + MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()}; + return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()}, + DRE->getSourceRange(), TheCall->getRParenLoc(), + Args, Op); + } + + ExprResult Sema::BuildAtomicExpr( + SourceRange CallRange, SourceRange ExprRange, SourceLocation RParenLoc, + MultiExprArg Args, AtomicExpr::AtomicOp Op, + AtomicArgumentOrder ArgOrder) { + // All the non-OpenCL operations take one of the following forms. + // The OpenCL operations take the __c11 forms with one extra argument for + // synchronization scope. + enum { + // C __c11_atomic_init(A *, C) + Init, + + // C __c11_atomic_load(A *, int) + Load, + + // void __atomic_load(A *, CP, int) + LoadCopy, + + // void __atomic_store(A *, CP, int) + Copy, + + // C __c11_atomic_add(A *, M, int) + Arithmetic, + + // C __atomic_exchange_n(A *, CP, int) + Xchg, + + // void __atomic_exchange(A *, C *, CP, int) + GNUXchg, + + // bool __c11_atomic_compare_exchange_strong(A *, C *, CP, int, int) + C11CmpXchg, + + // bool __atomic_compare_exchange(A *, C *, CP, bool, int, int) + GNUCmpXchg + } Form = Init; + + const unsigned NumForm = GNUCmpXchg + 1; + const unsigned NumArgs[] = {2, 2, 3, 3, 3, 3, 4, 5, 6}; + const unsigned NumVals[] = {1, 0, 1, 1, 1, 1, 2, 2, 3}; + // where: + // C is an appropriate type, + // A is volatile _Atomic(C) for __c11 builtins and is C for GNU + // builtins, CP is C for __c11 builtins and GNU _n builtins and is C * + // otherwise, M is C if C is an integer, and ptrdiff_t if C is a + // pointer, and the int parameters are for orderings. + + static_assert(sizeof(NumArgs) / sizeof(NumArgs[0]) == NumForm && + sizeof(NumVals) / sizeof(NumVals[0]) == NumForm, + "need to update code for modified forms"); + static_assert(AtomicExpr::AO__c11_atomic_init == 0 && + AtomicExpr::AO__c11_atomic_fetch_min + 1 == + AtomicExpr::AO__atomic_load, + "need to update code for modified C11 atomics"); + bool IsOpenCL = Op >= AtomicExpr::AO__opencl_atomic_init && + Op <= AtomicExpr::AO__opencl_atomic_fetch_max; + bool IsHIP = Op >= AtomicExpr::AO__hip_atomic_load && + Op <= AtomicExpr::AO__hip_atomic_fetch_max; + bool IsC11 = (Op >= AtomicExpr::AO__c11_atomic_init && + Op <= AtomicExpr::AO__c11_atomic_fetch_min) || + IsOpenCL; + bool IsN = Op == AtomicExpr::AO__atomic_load_n || + Op == AtomicExpr::AO__atomic_store_n || + Op == AtomicExpr::AO__atomic_exchange_n || + Op == AtomicExpr::AO__atomic_compare_exchange_n; + bool IsAddSub = false; + + switch (Op) { + case AtomicExpr::AO__c11_atomic_init: + case AtomicExpr::AO__opencl_atomic_init: + Form = Init; + break; - case AtomicExpr::AO__c11_atomic_store: - case AtomicExpr::AO__opencl_atomic_store: - case AtomicExpr::AO__hip_atomic_store: - case AtomicExpr::AO__atomic_store: - case AtomicExpr::AO__atomic_store_n: - Form = Copy; - break; - case AtomicExpr::AO__hip_atomic_fetch_add: - case AtomicExpr::AO__hip_atomic_fetch_min: - case AtomicExpr::AO__hip_atomic_fetch_max: - case AtomicExpr::AO__c11_atomic_fetch_add: - case AtomicExpr::AO__c11_atomic_fetch_sub: - case AtomicExpr::AO__opencl_atomic_fetch_add: - case AtomicExpr::AO__opencl_atomic_fetch_sub: - case AtomicExpr::AO__atomic_fetch_add: - case AtomicExpr::AO__atomic_fetch_sub: - case AtomicExpr::AO__atomic_add_fetch: - case AtomicExpr::AO__atomic_sub_fetch: - IsAddSub = true; - Form = Arithmetic; - break; - case AtomicExpr::AO__c11_atomic_fetch_and: - case AtomicExpr::AO__c11_atomic_fetch_or: - case AtomicExpr::AO__c11_atomic_fetch_xor: - case AtomicExpr::AO__hip_atomic_fetch_and: - case AtomicExpr::AO__hip_atomic_fetch_or: - case AtomicExpr::AO__hip_atomic_fetch_xor: - case AtomicExpr::AO__c11_atomic_fetch_nand: - case AtomicExpr::AO__opencl_atomic_fetch_and: - case AtomicExpr::AO__opencl_atomic_fetch_or: - case AtomicExpr::AO__opencl_atomic_fetch_xor: - case AtomicExpr::AO__atomic_fetch_and: - case AtomicExpr::AO__atomic_fetch_or: - case AtomicExpr::AO__atomic_fetch_xor: - case AtomicExpr::AO__atomic_fetch_nand: - case AtomicExpr::AO__atomic_and_fetch: - case AtomicExpr::AO__atomic_or_fetch: - case AtomicExpr::AO__atomic_xor_fetch: - case AtomicExpr::AO__atomic_nand_fetch: - Form = Arithmetic; - break; - case AtomicExpr::AO__c11_atomic_fetch_min: - case AtomicExpr::AO__c11_atomic_fetch_max: - case AtomicExpr::AO__opencl_atomic_fetch_min: - case AtomicExpr::AO__opencl_atomic_fetch_max: - case AtomicExpr::AO__atomic_min_fetch: - case AtomicExpr::AO__atomic_max_fetch: - case AtomicExpr::AO__atomic_fetch_min: - case AtomicExpr::AO__atomic_fetch_max: - Form = Arithmetic; - break; + case AtomicExpr::AO__c11_atomic_load: + case AtomicExpr::AO__opencl_atomic_load: + case AtomicExpr::AO__hip_atomic_load: + case AtomicExpr::AO__atomic_load_n: + Form = Load; + break; - case AtomicExpr::AO__c11_atomic_exchange: - case AtomicExpr::AO__hip_atomic_exchange: - case AtomicExpr::AO__opencl_atomic_exchange: - case AtomicExpr::AO__atomic_exchange_n: - Form = Xchg; - break; + case AtomicExpr::AO__atomic_load: + Form = LoadCopy; + break; - case AtomicExpr::AO__atomic_exchange: - Form = GNUXchg; - break; + case AtomicExpr::AO__c11_atomic_store: + case AtomicExpr::AO__opencl_atomic_store: + case AtomicExpr::AO__hip_atomic_store: + case AtomicExpr::AO__atomic_store: + case AtomicExpr::AO__atomic_store_n: + Form = Copy; + break; + case AtomicExpr::AO__hip_atomic_fetch_add: + case AtomicExpr::AO__hip_atomic_fetch_min: + case AtomicExpr::AO__hip_atomic_fetch_max: + case AtomicExpr::AO__c11_atomic_fetch_add: + case AtomicExpr::AO__c11_atomic_fetch_sub: + case AtomicExpr::AO__opencl_atomic_fetch_add: + case AtomicExpr::AO__opencl_atomic_fetch_sub: + case AtomicExpr::AO__atomic_fetch_add: + case AtomicExpr::AO__atomic_fetch_sub: + case AtomicExpr::AO__atomic_add_fetch: + case AtomicExpr::AO__atomic_sub_fetch: + IsAddSub = true; + Form = Arithmetic; + break; + case AtomicExpr::AO__c11_atomic_fetch_and: + case AtomicExpr::AO__c11_atomic_fetch_or: + case AtomicExpr::AO__c11_atomic_fetch_xor: + case AtomicExpr::AO__hip_atomic_fetch_and: + case AtomicExpr::AO__hip_atomic_fetch_or: + case AtomicExpr::AO__hip_atomic_fetch_xor: + case AtomicExpr::AO__c11_atomic_fetch_nand: + case AtomicExpr::AO__opencl_atomic_fetch_and: + case AtomicExpr::AO__opencl_atomic_fetch_or: + case AtomicExpr::AO__opencl_atomic_fetch_xor: + case AtomicExpr::AO__atomic_fetch_and: + case AtomicExpr::AO__atomic_fetch_or: + case AtomicExpr::AO__atomic_fetch_xor: + case AtomicExpr::AO__atomic_fetch_nand: + case AtomicExpr::AO__atomic_and_fetch: + case AtomicExpr::AO__atomic_or_fetch: + case AtomicExpr::AO__atomic_xor_fetch: + case AtomicExpr::AO__atomic_nand_fetch: + Form = Arithmetic; + break; + case AtomicExpr::AO__c11_atomic_fetch_min: + case AtomicExpr::AO__c11_atomic_fetch_max: + case AtomicExpr::AO__opencl_atomic_fetch_min: + case AtomicExpr::AO__opencl_atomic_fetch_max: + case AtomicExpr::AO__atomic_min_fetch: + case AtomicExpr::AO__atomic_max_fetch: + case AtomicExpr::AO__atomic_fetch_min: + case AtomicExpr::AO__atomic_fetch_max: + Form = Arithmetic; + break; - case AtomicExpr::AO__c11_atomic_compare_exchange_strong: - case AtomicExpr::AO__c11_atomic_compare_exchange_weak: - case AtomicExpr::AO__hip_atomic_compare_exchange_strong: - case AtomicExpr::AO__opencl_atomic_compare_exchange_strong: - case AtomicExpr::AO__opencl_atomic_compare_exchange_weak: - case AtomicExpr::AO__hip_atomic_compare_exchange_weak: - Form = C11CmpXchg; - break; + case AtomicExpr::AO__c11_atomic_exchange: + case AtomicExpr::AO__hip_atomic_exchange: + case AtomicExpr::AO__opencl_atomic_exchange: + case AtomicExpr::AO__atomic_exchange_n: + Form = Xchg; + break; - case AtomicExpr::AO__atomic_compare_exchange: - case AtomicExpr::AO__atomic_compare_exchange_n: - Form = GNUCmpXchg; - break; - } + case AtomicExpr::AO__atomic_exchange: + Form = GNUXchg; + break; - unsigned AdjustedNumArgs = NumArgs[Form]; - if ((IsOpenCL || IsHIP) && Op != AtomicExpr::AO__opencl_atomic_init) - ++AdjustedNumArgs; - // Check we have the right number of arguments. - if (Args.size() < AdjustedNumArgs) { - Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args) - << 0 << AdjustedNumArgs << static_cast(Args.size()) - << ExprRange; - return ExprError(); - } else if (Args.size() > AdjustedNumArgs) { - Diag(Args[AdjustedNumArgs]->getBeginLoc(), - diag::err_typecheck_call_too_many_args) - << 0 << AdjustedNumArgs << static_cast(Args.size()) - << ExprRange; - return ExprError(); - } + case AtomicExpr::AO__c11_atomic_compare_exchange_strong: + case AtomicExpr::AO__c11_atomic_compare_exchange_weak: + case AtomicExpr::AO__hip_atomic_compare_exchange_strong: + case AtomicExpr::AO__opencl_atomic_compare_exchange_strong: + case AtomicExpr::AO__opencl_atomic_compare_exchange_weak: + case AtomicExpr::AO__hip_atomic_compare_exchange_weak: + Form = C11CmpXchg; + break; - // Inspect the first argument of the atomic operation. - Expr *Ptr = Args[0]; - ExprResult ConvertedPtr = DefaultFunctionArrayLvalueConversion(Ptr); - if (ConvertedPtr.isInvalid()) - return ExprError(); + case AtomicExpr::AO__atomic_compare_exchange: + case AtomicExpr::AO__atomic_compare_exchange_n: + Form = GNUCmpXchg; + break; + } - Ptr = ConvertedPtr.get(); - const PointerType *pointerType = Ptr->getType()->getAs(); - if (!pointerType) { - Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer) - << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } + unsigned AdjustedNumArgs = NumArgs[Form]; + if ((IsOpenCL || IsHIP) && Op != AtomicExpr::AO__opencl_atomic_init) + ++AdjustedNumArgs; + // Check we have the right number of arguments. + if (Args.size() < AdjustedNumArgs) { + Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args) + << 0 << AdjustedNumArgs << static_cast(Args.size()) + << ExprRange; + return ExprError(); + } else if (Args.size() > AdjustedNumArgs) { + Diag(Args[AdjustedNumArgs]->getBeginLoc(), + diag::err_typecheck_call_too_many_args) + << 0 << AdjustedNumArgs << static_cast(Args.size()) + << ExprRange; + return ExprError(); + } - // For a __c11 builtin, this should be a pointer to an _Atomic type. - QualType AtomTy = pointerType->getPointeeType(); // 'A' - QualType ValType = AtomTy; // 'C' - if (IsC11) { - if (!AtomTy->isAtomicType()) { - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic) - << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } - if ((Form != Load && Form != LoadCopy && AtomTy.isConstQualified()) || - AtomTy.getAddressSpace() == LangAS::opencl_constant) { - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_atomic) - << (AtomTy.isConstQualified() ? 0 : 1) << Ptr->getType() - << Ptr->getSourceRange(); - return ExprError(); - } - ValType = AtomTy->castAs()->getValueType(); - } else if (Form != Load && Form != LoadCopy) { - if (ValType.isConstQualified()) { - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_pointer) - << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } - } + // Inspect the first argument of the atomic operation. + Expr *Ptr = Args[0]; + ExprResult ConvertedPtr = DefaultFunctionArrayLvalueConversion(Ptr); + if (ConvertedPtr.isInvalid()) + return ExprError(); - // For an arithmetic operation, the implied arithmetic must be well-formed. - if (Form == Arithmetic) { - // GCC does not enforce these rules for GNU atomics, but we do to help catch - // trivial type errors. - auto IsAllowedValueType = [&](QualType ValType) { - if (ValType->isIntegerType()) - return true; - if (ValType->isPointerType()) - return true; - if (!ValType->isFloatingType()) - return false; - // LLVM Parser does not allow atomicrmw with x86_fp80 type. - if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) && - &Context.getTargetInfo().getLongDoubleFormat() == - &llvm::APFloat::x87DoubleExtended()) - return false; - return true; - }; - if (IsAddSub && !IsAllowedValueType(ValType)) { - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_ptr_or_fp) - << IsC11 << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } - if (!IsAddSub && !ValType->isIntegerType()) { - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int) - << IsC11 << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } - if (IsC11 && ValType->isPointerType() && - RequireCompleteType(Ptr->getBeginLoc(), ValType->getPointeeType(), - diag::err_incomplete_type)) { - return ExprError(); - } - } else if (IsN && !ValType->isIntegerType() && !ValType->isPointerType()) { - // For __atomic_*_n operations, the value type must be a scalar integral or - // pointer type which is 1, 2, 4, 8 or 16 bytes in length. - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr) - << IsC11 << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } + Ptr = ConvertedPtr.get(); + const PointerType *pointerType = Ptr->getType()->getAs(); + if (!pointerType) { + Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } - if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) && - !AtomTy->isScalarType()) { - // For GNU atomics, require a trivially-copyable type. This is not part of - // the GNU atomics specification but we enforce it for consistency with - // other atomics which generally all require a trivially-copyable type. This - // is because atomics just copy bits. - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_trivial_copy) - << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); - } + // For a __c11 builtin, this should be a pointer to an _Atomic type. + QualType AtomTy = pointerType->getPointeeType(); // 'A' + QualType ValType = AtomTy; // 'C' + if (IsC11) { + if (!AtomTy->isAtomicType()) { + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + if ((Form != Load && Form != LoadCopy && AtomTy.isConstQualified()) || + AtomTy.getAddressSpace() == LangAS::opencl_constant) { + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_atomic) + << (AtomTy.isConstQualified() ? 0 : 1) << Ptr->getType() + << Ptr->getSourceRange(); + return ExprError(); + } + ValType = AtomTy->castAs()->getValueType(); + } else if (Form != Load && Form != LoadCopy) { + if (ValType.isConstQualified()) { + Diag(ExprRange.getBegin(), + diag::err_atomic_op_needs_non_const_pointer) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + } - switch (ValType.getObjCLifetime()) { - case Qualifiers::OCL_None: - case Qualifiers::OCL_ExplicitNone: - // okay - break; + // For an arithmetic operation, the implied arithmetic must be + // well-formed. + if (Form == Arithmetic) { + // GCC does not enforce these rules for GNU atomics, but we do to help + // catch trivial type errors. + auto IsAllowedValueType = [&](QualType ValType) { + if (ValType->isIntegerType()) + return true; + if (ValType->isPointerType()) + return true; + if (!ValType->isFloatingType()) + return false; + // LLVM Parser does not allow atomicrmw with x86_fp80 type. + if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) && + &Context.getTargetInfo().getLongDoubleFormat() == + &llvm::APFloat::x87DoubleExtended()) + return false; + return true; + }; + if (IsAddSub && !IsAllowedValueType(ValType)) { + Diag(ExprRange.getBegin(), + diag::err_atomic_op_needs_atomic_int_ptr_or_fp) + << IsC11 << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + if (!IsAddSub && !ValType->isIntegerType()) { + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int) + << IsC11 << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + if (IsC11 && ValType->isPointerType() && + RequireCompleteType(Ptr->getBeginLoc(), ValType->getPointeeType(), + diag::err_incomplete_type)) { + return ExprError(); + } + } else if (IsN && !ValType->isIntegerType() && + !ValType->isPointerType()) { + // For __atomic_*_n operations, the value type must be a scalar integral + // or pointer type which is 1, 2, 4, 8 or 16 bytes in length. + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr) + << IsC11 << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } - case Qualifiers::OCL_Weak: - case Qualifiers::OCL_Strong: - case Qualifiers::OCL_Autoreleasing: - // FIXME: Can this happen? By this point, ValType should be known - // to be trivially copyable. - Diag(ExprRange.getBegin(), diag::err_arc_atomic_ownership) - << ValType << Ptr->getSourceRange(); - return ExprError(); - } + if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) && + !AtomTy->isScalarType()) { + // For GNU atomics, require a trivially-copyable type. This is not part + // of the GNU atomics specification but we enforce it for consistency + // with other atomics which generally all require a trivially-copyable + // type. This is because atomics just copy bits. + Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_trivial_copy) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } - // All atomic operations have an overload which takes a pointer to a volatile - // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself - // into the result or the other operands. Similarly atomic_load takes a - // pointer to a const 'A'. - ValType.removeLocalVolatile(); - ValType.removeLocalConst(); - QualType ResultType = ValType; - if (Form == Copy || Form == LoadCopy || Form == GNUXchg || - Form == Init) - ResultType = Context.VoidTy; - else if (Form == C11CmpXchg || Form == GNUCmpXchg) - ResultType = Context.BoolTy; + switch (ValType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // okay + break; - // The type of a parameter passed 'by value'. In the GNU atomics, such - // arguments are actually passed as pointers. - QualType ByValType = ValType; // 'CP' - bool IsPassedByAddress = false; - if (!IsC11 && !IsHIP && !IsN) { - ByValType = Ptr->getType(); - IsPassedByAddress = true; - } - - SmallVector APIOrderedArgs; - if (ArgOrder == Sema::AtomicArgumentOrder::AST) { - APIOrderedArgs.push_back(Args[0]); - switch (Form) { - case Init: - case Load: - APIOrderedArgs.push_back(Args[1]); // Val1/Order - break; - case LoadCopy: - case Copy: - case Arithmetic: - case Xchg: - APIOrderedArgs.push_back(Args[2]); // Val1 - APIOrderedArgs.push_back(Args[1]); // Order - break; - case GNUXchg: - APIOrderedArgs.push_back(Args[2]); // Val1 - APIOrderedArgs.push_back(Args[3]); // Val2 - APIOrderedArgs.push_back(Args[1]); // Order - break; - case C11CmpXchg: - APIOrderedArgs.push_back(Args[2]); // Val1 - APIOrderedArgs.push_back(Args[4]); // Val2 - APIOrderedArgs.push_back(Args[1]); // Order - APIOrderedArgs.push_back(Args[3]); // OrderFail - break; - case GNUCmpXchg: - APIOrderedArgs.push_back(Args[2]); // Val1 - APIOrderedArgs.push_back(Args[4]); // Val2 - APIOrderedArgs.push_back(Args[5]); // Weak - APIOrderedArgs.push_back(Args[1]); // Order - APIOrderedArgs.push_back(Args[3]); // OrderFail - break; - } - } else - APIOrderedArgs.append(Args.begin(), Args.end()); - - // The first argument's non-CV pointer type is used to deduce the type of - // subsequent arguments, except for: - // - weak flag (always converted to bool) - // - memory order (always converted to int) - // - scope (always converted to int) - for (unsigned i = 0; i != APIOrderedArgs.size(); ++i) { - QualType Ty; - if (i < NumVals[Form] + 1) { - switch (i) { - case 0: - // The first argument is always a pointer. It has a fixed type. - // It is always dereferenced, a nullptr is undefined. - CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin()); - // Nothing else to do: we already know all we want about this pointer. - continue; - case 1: - // The second argument is the non-atomic operand. For arithmetic, this - // is always passed by value, and for a compare_exchange it is always - // passed by address. For the rest, GNU uses by-address and C11 uses - // by-value. - assert(Form != Load); - if (Form == Arithmetic && ValType->isPointerType()) - Ty = Context.getPointerDiffType(); - else if (Form == Init || Form == Arithmetic) - Ty = ValType; - else if (Form == Copy || Form == Xchg) { - if (IsPassedByAddress) { - // The value pointer is always dereferenced, a nullptr is undefined. + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + // FIXME: Can this happen? By this point, ValType should be known + // to be trivially copyable. + Diag(ExprRange.getBegin(), diag::err_arc_atomic_ownership) + << ValType << Ptr->getSourceRange(); + return ExprError(); + } + + // All atomic operations have an overload which takes a pointer to a + // volatile 'A'. We shouldn't let the volatile-ness of the pointee-type + // inject itself into the result or the other operands. Similarly + // atomic_load takes a pointer to a const 'A'. + ValType.removeLocalVolatile(); + ValType.removeLocalConst(); + QualType ResultType = ValType; + if (Form == Copy || Form == LoadCopy || Form == GNUXchg || Form == Init) + ResultType = Context.VoidTy; + else if (Form == C11CmpXchg || Form == GNUCmpXchg) + ResultType = Context.BoolTy; + + // The type of a parameter passed 'by value'. In the GNU atomics, such + // arguments are actually passed as pointers. + QualType ByValType = ValType; // 'CP' + bool IsPassedByAddress = false; + if (!IsC11 && !IsHIP && !IsN) { + ByValType = Ptr->getType(); + IsPassedByAddress = true; + } + + SmallVector APIOrderedArgs; + if (ArgOrder == Sema::AtomicArgumentOrder::AST) { + APIOrderedArgs.push_back(Args[0]); + switch (Form) { + case Init: + case Load: + APIOrderedArgs.push_back(Args[1]); // Val1/Order + break; + case LoadCopy: + case Copy: + case Arithmetic: + case Xchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[1]); // Order + break; + case GNUXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[3]); // Val2 + APIOrderedArgs.push_back(Args[1]); // Order + break; + case C11CmpXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[4]); // Val2 + APIOrderedArgs.push_back(Args[1]); // Order + APIOrderedArgs.push_back(Args[3]); // OrderFail + break; + case GNUCmpXchg: + APIOrderedArgs.push_back(Args[2]); // Val1 + APIOrderedArgs.push_back(Args[4]); // Val2 + APIOrderedArgs.push_back(Args[5]); // Weak + APIOrderedArgs.push_back(Args[1]); // Order + APIOrderedArgs.push_back(Args[3]); // OrderFail + break; + } + } else + APIOrderedArgs.append(Args.begin(), Args.end()); + + // The first argument's non-CV pointer type is used to deduce the type of + // subsequent arguments, except for: + // - weak flag (always converted to bool) + // - memory order (always converted to int) + // - scope (always converted to int) + for (unsigned i = 0; i != APIOrderedArgs.size(); ++i) { + QualType Ty; + if (i < NumVals[Form] + 1) { + switch (i) { + case 0: + // The first argument is always a pointer. It has a fixed type. + // It is always dereferenced, a nullptr is undefined. CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin()); + // Nothing else to do: we already know all we want about this + // pointer. + continue; + case 1: + // The second argument is the non-atomic operand. For arithmetic, + // this is always passed by value, and for a compare_exchange it is + // always passed by address. For the rest, GNU uses by-address and + // C11 uses by-value. + assert(Form != Load); + if (Form == Arithmetic && ValType->isPointerType()) + Ty = Context.getPointerDiffType(); + else if (Form == Init || Form == Arithmetic) + Ty = ValType; + else if (Form == Copy || Form == Xchg) { + if (IsPassedByAddress) { + // The value pointer is always dereferenced, a nullptr is + // undefined. + CheckNonNullArgument(*this, APIOrderedArgs[i], + ExprRange.getBegin()); + } + Ty = ByValType; + } else { + Expr *ValArg = APIOrderedArgs[i]; + // The value pointer is always dereferenced, a nullptr is + // undefined. + CheckNonNullArgument(*this, ValArg, ExprRange.getBegin()); + LangAS AS = LangAS::Default; + // Keep address space of non-atomic pointer type. + if (const PointerType *PtrTy = + ValArg->getType()->getAs()) { + AS = PtrTy->getPointeeType().getAddressSpace(); + } + Ty = Context.getPointerType(Context.getAddrSpaceQualType( + ValType.getUnqualifiedType(), AS)); + } + break; + case 2: + // The third argument to compare_exchange / GNU exchange is the + // desired value, either by-value (for the C11 and *_n variant) or + // as a pointer. + if (IsPassedByAddress) + CheckNonNullArgument(*this, APIOrderedArgs[i], + ExprRange.getBegin()); + Ty = ByValType; + break; + case 3: + // The fourth argument to GNU compare_exchange is a 'weak' flag. + Ty = Context.BoolTy; + break; } - Ty = ByValType; } else { - Expr *ValArg = APIOrderedArgs[i]; - // The value pointer is always dereferenced, a nullptr is undefined. - CheckNonNullArgument(*this, ValArg, ExprRange.getBegin()); - LangAS AS = LangAS::Default; - // Keep address space of non-atomic pointer type. - if (const PointerType *PtrTy = - ValArg->getType()->getAs()) { - AS = PtrTy->getPointeeType().getAddressSpace(); - } - Ty = Context.getPointerType( - Context.getAddrSpaceQualType(ValType.getUnqualifiedType(), AS)); + // The order(s) and scope are always converted to int. + Ty = Context.IntTy; } + + InitializedEntity Entity = + InitializedEntity::InitializeParameter(Context, Ty, false); + ExprResult Arg = APIOrderedArgs[i]; + Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (Arg.isInvalid()) + return true; + APIOrderedArgs[i] = Arg.get(); + } + + // Permute the arguments into a 'consistent' order. + SmallVector SubExprs; + SubExprs.push_back(Ptr); + switch (Form) { + case Init: + // Note, AtomicExpr::getVal1() has a special case for this atomic. + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + break; + case Load: + SubExprs.push_back(APIOrderedArgs[1]); // Order + break; + case LoadCopy: + case Copy: + case Arithmetic: + case Xchg: + SubExprs.push_back(APIOrderedArgs[2]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 break; - case 2: - // The third argument to compare_exchange / GNU exchange is the desired - // value, either by-value (for the C11 and *_n variant) or as a pointer. - if (IsPassedByAddress) - CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin()); - Ty = ByValType; + case GNUXchg: + // Note, AtomicExpr::getVal2() has a special case for this atomic. + SubExprs.push_back(APIOrderedArgs[3]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[2]); // Val2 break; - case 3: - // The fourth argument to GNU compare_exchange is a 'weak' flag. - Ty = Context.BoolTy; + case C11CmpXchg: + SubExprs.push_back(APIOrderedArgs[3]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[4]); // OrderFail + SubExprs.push_back(APIOrderedArgs[2]); // Val2 + break; + case GNUCmpXchg: + SubExprs.push_back(APIOrderedArgs[4]); // Order + SubExprs.push_back(APIOrderedArgs[1]); // Val1 + SubExprs.push_back(APIOrderedArgs[5]); // OrderFail + SubExprs.push_back(APIOrderedArgs[2]); // Val2 + SubExprs.push_back(APIOrderedArgs[3]); // Weak break; } - } else { - // The order(s) and scope are always converted to int. - Ty = Context.IntTy; - } - - InitializedEntity Entity = - InitializedEntity::InitializeParameter(Context, Ty, false); - ExprResult Arg = APIOrderedArgs[i]; - Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); - if (Arg.isInvalid()) - return true; - APIOrderedArgs[i] = Arg.get(); - } - - // Permute the arguments into a 'consistent' order. - SmallVector SubExprs; - SubExprs.push_back(Ptr); - switch (Form) { - case Init: - // Note, AtomicExpr::getVal1() has a special case for this atomic. - SubExprs.push_back(APIOrderedArgs[1]); // Val1 - break; - case Load: - SubExprs.push_back(APIOrderedArgs[1]); // Order - break; - case LoadCopy: - case Copy: - case Arithmetic: - case Xchg: - SubExprs.push_back(APIOrderedArgs[2]); // Order - SubExprs.push_back(APIOrderedArgs[1]); // Val1 - break; - case GNUXchg: - // Note, AtomicExpr::getVal2() has a special case for this atomic. - SubExprs.push_back(APIOrderedArgs[3]); // Order - SubExprs.push_back(APIOrderedArgs[1]); // Val1 - SubExprs.push_back(APIOrderedArgs[2]); // Val2 - break; - case C11CmpXchg: - SubExprs.push_back(APIOrderedArgs[3]); // Order - SubExprs.push_back(APIOrderedArgs[1]); // Val1 - SubExprs.push_back(APIOrderedArgs[4]); // OrderFail - SubExprs.push_back(APIOrderedArgs[2]); // Val2 - break; - case GNUCmpXchg: - SubExprs.push_back(APIOrderedArgs[4]); // Order - SubExprs.push_back(APIOrderedArgs[1]); // Val1 - SubExprs.push_back(APIOrderedArgs[5]); // OrderFail - SubExprs.push_back(APIOrderedArgs[2]); // Val2 - SubExprs.push_back(APIOrderedArgs[3]); // Weak - break; - } - if (SubExprs.size() >= 2 && Form != Init) { - if (Optional Result = - SubExprs[1]->getIntegerConstantExpr(Context)) - if (!isValidOrderingForOp(Result->getSExtValue(), Op)) - Diag(SubExprs[1]->getBeginLoc(), - diag::warn_atomic_op_has_invalid_memory_order) - << SubExprs[1]->getSourceRange(); - } - - if (auto ScopeModel = AtomicExpr::getScopeModel(Op)) { - auto *Scope = Args[Args.size() - 1]; - if (Optional Result = - Scope->getIntegerConstantExpr(Context)) { - if (!ScopeModel->isValid(Result->getZExtValue())) - Diag(Scope->getBeginLoc(), diag::err_atomic_op_has_invalid_synch_scope) - << Scope->getSourceRange(); - } - SubExprs.push_back(Scope); - } - - AtomicExpr *AE = new (Context) - AtomicExpr(ExprRange.getBegin(), SubExprs, ResultType, Op, RParenLoc); - - if ((Op == AtomicExpr::AO__c11_atomic_load || - Op == AtomicExpr::AO__c11_atomic_store || - Op == AtomicExpr::AO__opencl_atomic_load || - Op == AtomicExpr::AO__hip_atomic_load || - Op == AtomicExpr::AO__opencl_atomic_store || - Op == AtomicExpr::AO__hip_atomic_store) && - Context.AtomicUsesUnsupportedLibcall(AE)) - Diag(AE->getBeginLoc(), diag::err_atomic_load_store_uses_lib) - << ((Op == AtomicExpr::AO__c11_atomic_load || - Op == AtomicExpr::AO__opencl_atomic_load || - Op == AtomicExpr::AO__hip_atomic_load) - ? 0 - : 1); - - if (ValType->isBitIntType()) { - Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit); - return ExprError(); - } + if (SubExprs.size() >= 2 && Form != Init) { + if (Optional Result = + SubExprs[1]->getIntegerConstantExpr(Context)) + if (!isValidOrderingForOp(Result->getSExtValue(), Op)) + Diag(SubExprs[1]->getBeginLoc(), + diag::warn_atomic_op_has_invalid_memory_order) + << SubExprs[1]->getSourceRange(); + } - return AE; -} + if (auto ScopeModel = AtomicExpr::getScopeModel(Op)) { + auto *Scope = Args[Args.size() - 1]; + if (Optional Result = + Scope->getIntegerConstantExpr(Context)) { + if (!ScopeModel->isValid(Result->getZExtValue())) + Diag(Scope->getBeginLoc(), + diag::err_atomic_op_has_invalid_synch_scope) + << Scope->getSourceRange(); + } + SubExprs.push_back(Scope); + } -/// checkBuiltinArgument - Given a call to a builtin function, perform -/// normal type-checking on the given argument, updating the call in -/// place. This is useful when a builtin function requires custom -/// type-checking for some of its arguments but not necessarily all of -/// them. -/// -/// Returns true on error. -static bool checkBuiltinArgument(Sema &S, CallExpr *E, unsigned ArgIndex) { - FunctionDecl *Fn = E->getDirectCallee(); - assert(Fn && "builtin call without direct callee!"); + AtomicExpr *AE = new (Context) + AtomicExpr(ExprRange.getBegin(), SubExprs, ResultType, Op, RParenLoc); + + if ((Op == AtomicExpr::AO__c11_atomic_load || + Op == AtomicExpr::AO__c11_atomic_store || + Op == AtomicExpr::AO__opencl_atomic_load || + Op == AtomicExpr::AO__hip_atomic_load || + Op == AtomicExpr::AO__opencl_atomic_store || + Op == AtomicExpr::AO__hip_atomic_store) && + Context.AtomicUsesUnsupportedLibcall(AE)) + Diag(AE->getBeginLoc(), diag::err_atomic_load_store_uses_lib) + << ((Op == AtomicExpr::AO__c11_atomic_load || + Op == AtomicExpr::AO__opencl_atomic_load || + Op == AtomicExpr::AO__hip_atomic_load) + ? 0 + : 1); + + if (ValType->isBitIntType()) { + Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit); + return ExprError(); + } - ParmVarDecl *Param = Fn->getParamDecl(ArgIndex); - InitializedEntity Entity = - InitializedEntity::InitializeParameter(S.Context, Param); + return AE; + } - ExprResult Arg = E->getArg(0); - Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg); - if (Arg.isInvalid()) - return true; + /// checkBuiltinArgument - Given a call to a builtin function, perform + /// normal type-checking on the given argument, updating the call in + /// place. This is useful when a builtin function requires custom + /// type-checking for some of its arguments but not necessarily all of + /// them. + /// + /// Returns true on error. + static bool checkBuiltinArgument(Sema & S, CallExpr * E, + unsigned ArgIndex) { + FunctionDecl *Fn = E->getDirectCallee(); + assert(Fn && "builtin call without direct callee!"); - E->setArg(ArgIndex, Arg.get()); - return false; -} + ParmVarDecl *Param = Fn->getParamDecl(ArgIndex); + InitializedEntity Entity = + InitializedEntity::InitializeParameter(S.Context, Param); -/// We have a call to a function like __sync_fetch_and_add, which is an -/// overloaded function based on the pointer type of its first argument. -/// The main BuildCallExpr routines have already promoted the types of -/// arguments because all of these calls are prototyped as void(...). -/// -/// This function goes through and does final semantic checking for these -/// builtins, as well as generating any warnings. -ExprResult -Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { - CallExpr *TheCall = static_cast(TheCallResult.get()); - Expr *Callee = TheCall->getCallee(); - DeclRefExpr *DRE = cast(Callee->IgnoreParenCasts()); - FunctionDecl *FDecl = cast(DRE->getDecl()); + ExprResult Arg = E->getArg(0); + Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (Arg.isInvalid()) + return true; - // Ensure that we have at least one argument to do type inference from. - if (TheCall->getNumArgs() < 1) { - Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least) - << 0 << 1 << TheCall->getNumArgs() << Callee->getSourceRange(); - return ExprError(); - } + E->setArg(ArgIndex, Arg.get()); + return false; + } - // Inspect the first argument of the atomic builtin. This should always be - // a pointer type, whose element is an integral scalar or pointer type. - // Because it is a pointer type, we don't have to worry about any implicit - // casts here. - // FIXME: We don't allow floating point scalars as input. - Expr *FirstArg = TheCall->getArg(0); - ExprResult FirstArgResult = DefaultFunctionArrayLvalueConversion(FirstArg); - if (FirstArgResult.isInvalid()) - return ExprError(); - FirstArg = FirstArgResult.get(); - TheCall->setArg(0, FirstArg); + /// We have a call to a function like __sync_fetch_and_add, which is an + /// overloaded function based on the pointer type of its first argument. + /// The main BuildCallExpr routines have already promoted the types of + /// arguments because all of these calls are prototyped as void(...). + /// + /// This function goes through and does final semantic checking for these + /// builtins, as well as generating any warnings. + ExprResult Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { + CallExpr *TheCall = static_cast(TheCallResult.get()); + Expr *Callee = TheCall->getCallee(); + DeclRefExpr *DRE = cast(Callee->IgnoreParenCasts()); + FunctionDecl *FDecl = cast(DRE->getDecl()); + + // Ensure that we have at least one argument to do type inference from. + if (TheCall->getNumArgs() < 1) { + Diag(TheCall->getEndLoc(), + diag::err_typecheck_call_too_few_args_at_least) + << 0 << 1 << TheCall->getNumArgs() << Callee->getSourceRange(); + return ExprError(); + } - const PointerType *pointerType = FirstArg->getType()->getAs(); - if (!pointerType) { - Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer) - << FirstArg->getType() << FirstArg->getSourceRange(); - return ExprError(); - } + // Inspect the first argument of the atomic builtin. This should always + // be a pointer type, whose element is an integral scalar or pointer type. + // Because it is a pointer type, we don't have to worry about any implicit + // casts here. + // FIXME: We don't allow floating point scalars as input. + Expr *FirstArg = TheCall->getArg(0); + ExprResult FirstArgResult = + DefaultFunctionArrayLvalueConversion(FirstArg); + if (FirstArgResult.isInvalid()) + return ExprError(); + FirstArg = FirstArgResult.get(); + TheCall->setArg(0, FirstArg); + + const PointerType *pointerType = + FirstArg->getType()->getAs(); + if (!pointerType) { + Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer) + << FirstArg->getType() << FirstArg->getSourceRange(); + return ExprError(); + } - QualType ValType = pointerType->getPointeeType(); - if (!ValType->isIntegerType() && !ValType->isAnyPointerType() && - !ValType->isBlockPointerType()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer_intptr) - << FirstArg->getType() << FirstArg->getSourceRange(); - return ExprError(); - } + QualType ValType = pointerType->getPointeeType(); + if (!ValType->isIntegerType() && !ValType->isAnyPointerType() && + !ValType->isBlockPointerType()) { + Diag(DRE->getBeginLoc(), + diag::err_atomic_builtin_must_be_pointer_intptr) + << FirstArg->getType() << FirstArg->getSourceRange(); + return ExprError(); + } - if (ValType.isConstQualified()) { - Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const) - << FirstArg->getType() << FirstArg->getSourceRange(); - return ExprError(); - } + if (ValType.isConstQualified()) { + Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const) + << FirstArg->getType() << FirstArg->getSourceRange(); + return ExprError(); + } - switch (ValType.getObjCLifetime()) { - case Qualifiers::OCL_None: - case Qualifiers::OCL_ExplicitNone: - // okay - break; + switch (ValType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // okay + break; - case Qualifiers::OCL_Weak: - case Qualifiers::OCL_Strong: - case Qualifiers::OCL_Autoreleasing: - Diag(DRE->getBeginLoc(), diag::err_arc_atomic_ownership) - << ValType << FirstArg->getSourceRange(); - return ExprError(); - } + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + Diag(DRE->getBeginLoc(), diag::err_arc_atomic_ownership) + << ValType << FirstArg->getSourceRange(); + return ExprError(); + } - // Strip any qualifiers off ValType. - ValType = ValType.getUnqualifiedType(); + // Strip any qualifiers off ValType. + ValType = ValType.getUnqualifiedType(); - // The majority of builtins return a value, but a few have special return - // types, so allow them to override appropriately below. - QualType ResultType = ValType; + // The majority of builtins return a value, but a few have special return + // types, so allow them to override appropriately below. + QualType ResultType = ValType; - // We need to figure out which concrete builtin this maps onto. For example, - // __sync_fetch_and_add with a 2 byte object turns into - // __sync_fetch_and_add_2. + // We need to figure out which concrete builtin this maps onto. For + // example, + // __sync_fetch_and_add with a 2 byte object turns into + // __sync_fetch_and_add_2. #define BUILTIN_ROW(x) \ { Builtin::BI##x##_1, Builtin::BI##x##_2, Builtin::BI##x##_4, \ Builtin::BI##x##_8, Builtin::BI##x##_16 } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4345,13 +4345,6 @@ return; uint64_t AlignVal = Alignment.getZExtValue(); - // 16 byte ByVal alignment not due to a vector member is not honoured by XL - // on AIX. Emit a warning here that users are generating binary incompatible - // code to be safe. - if (AlignVal >= 16 && isa(D) && - Context.getTargetInfo().getTriple().isOSAIX()) - Diag(AttrLoc, diag::warn_not_xl_compatible) << E->getSourceRange(); - // C++11 [dcl.align]p2: // -- if the constant expression evaluates to zero, the alignment // specifier shall have no effect diff --git a/clang/test/Analysis/padding_c.c b/clang/test/Analysis/padding_c.c --- a/clang/test/Analysis/padding_c.c +++ b/clang/test/Analysis/padding_c.c @@ -1,10 +1,8 @@ -// FIXME -Wno-aix-compat added temporarily while the diagnostic is being -// refined. -// RUN: %clang_analyze_cc1 -verify -Wno-aix-compat %s \ +// RUN: %clang_analyze_cc1 -verify %s \ // RUN: -analyzer-checker=optin.performance \ // RUN: -analyzer-config optin.performance.Padding:AllowedPad=2 -// RUN: not %clang_analyze_cc1 -verify -Wno-aix-compat %s \ +// RUN: not %clang_analyze_cc1 -verify %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=optin.performance.Padding \ // RUN: -analyzer-config optin.performance.Padding:AllowedPad=-10 \ diff --git a/clang/test/Analysis/padding_cpp.cpp b/clang/test/Analysis/padding_cpp.cpp --- a/clang/test/Analysis/padding_cpp.cpp +++ b/clang/test/Analysis/padding_cpp.cpp @@ -1,6 +1,4 @@ -// FIXME -Wno-aix-compat added temporarily while the diagnostic is being -// refined. -// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=2 -verify -Wno-aix-compat %s +// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=optin.performance -analyzer-config optin.performance.Padding:AllowedPad=2 -verify %s // Make sure that the C cases still work fine, even when compiled as C++. #include "padding_c.c" diff --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp --- a/clang/test/CXX/drs/dr6xx.cpp +++ b/clang/test/CXX/drs/dr6xx.cpp @@ -1,10 +1,8 @@ -// FIXME -Wno-aix-compat added temporarily while the diagnostic is being -// refined. -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -Wno-aix-compat -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -Wno-aix-compat -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -Wno-aix-compat -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -Wno-aix-compat -// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking -Wno-aix-compat +// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking +// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking +// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking +// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking +// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -fno-spell-checking namespace std { struct type_info {}; diff --git a/clang/test/Sema/aix-attr-align.c b/clang/test/Sema/aix-attr-align.c --- a/clang/test/Sema/aix-attr-align.c +++ b/clang/test/Sema/aix-attr-align.c @@ -5,18 +5,37 @@ // RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -verify=off -Wno-aix-compat -fsyntax-only %s // RUN: %clang_cc1 -triple powerpc64le-unknown-linux -verify=off -fsyntax-only %s -struct S { - int a[8] __attribute__((aligned(8))); // no-warning +// We do not warn on any declaration with a member aligned 16. Only when the struct is passed byval. +struct R { + int b[8] __attribute__((aligned(16))); // no-warning }; -struct T { - int a[4] __attribute__((aligned(16))); // expected-warning {{requesting an alignment of 16 bytes or greater for struct members is not binary compatible with IBM XL C/C++ for AIX 16.1.0 and older}} +struct S { + int a[8] __attribute__((aligned(8))); // no-warning + int b[8] __attribute__((aligned(16))); // expected-warning {{alignment of 16 bytes for a struct member is not binary compatible with IBM XL C/C++ for AIX 16.1.0 or older}} }; -struct U { - int a[2] __attribute__((aligned(32))); // expected-warning {{requesting an alignment of 16 bytes or greater for struct members is not binary compatible with IBM XL C/C++ for AIX 16.1.0 and older}} +struct T { + int a[8] __attribute__((aligned(8))); // no-warning + int b[8] __attribute__((aligned(4))); // no-warning }; int a[8] __attribute__((aligned(8))); // no-warning int b[4] __attribute__((aligned(16))); // no-warning -int c[2] __attribute__((aligned(32))); // no-warning + +void baz(int a, int b, int *c, int d, int *e, int f, struct S); +void jaz(int a, int b, int *c, int d, int *e, int f, struct T); +void vararg_baz(int a,...); +static void static_baz(int a, int b, int *c, int d, int *e, int f, struct S sp2) { + a = *sp2.b + *c + *e; +} + +void foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, + struct S s, struct T t) { + + baz(p1, p2, s.b, p3, b, p5, s); // expected-note {{passing byval argument 's' with potentially incompatible alignment here}} + jaz(p1, p2, a, p3, s.a, p5, t); // no-note + jaz(p1, p2, s.b, p3, b, p5, t); // no-note + vararg_baz(p1, p2, s.b, p3, b, p5, s); // no-note + static_baz(p1, p2, s.b, p3, b, p5, s); // no-note +} diff --git a/clang/test/SemaTemplate/instantiate-attr.cpp b/clang/test/SemaTemplate/instantiate-attr.cpp --- a/clang/test/SemaTemplate/instantiate-attr.cpp +++ b/clang/test/SemaTemplate/instantiate-attr.cpp @@ -1,7 +1,4 @@ -// FIXME -Wno-aix-compat added temporarily while the diagnostic is being -// refined. - -// RUN: %clang_cc1 -fsyntax-only -verify -Wno-aix-compat %s +// RUN: %clang_cc1 -fsyntax-only -verify %s // expected-no-diagnostics template struct A {