diff options
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/AST/ByteCode/Interp.cpp | 115 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Interp.h | 26 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpBuiltin.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp | 89 | ||||
| -rw-r--r-- | clang/lib/CodeGen/BackendUtil.cpp | 8 | ||||
| -rw-r--r-- | clang/lib/Driver/ToolChains/Flang.cpp | 7 | ||||
| -rw-r--r-- | clang/lib/Format/WhitespaceManager.cpp | 25 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaARM.cpp | 15 |
8 files changed, 209 insertions, 78 deletions
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 910868b..d640be0 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -870,7 +870,8 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return true; } -bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { +bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + bool WillBeActivated) { if (!Ptr.isBlockPointer() || Ptr.isZero()) return false; @@ -885,7 +886,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; if (!CheckRange(S, OpPC, Ptr, AK_Assign)) return false; - if (!CheckActive(S, OpPC, Ptr, AK_Assign)) + if (!WillBeActivated && !CheckActive(S, OpPC, Ptr, AK_Assign)) return false; if (!CheckGlobal(S, OpPC, Ptr)) return false; @@ -932,6 +933,15 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { if (F->isValid() && F->hasBody() && F->isConstexpr()) return true; + const FunctionDecl *DiagDecl = F->getDecl(); + const FunctionDecl *Definition = nullptr; + DiagDecl->getBody(Definition); + + if (!Definition && S.checkingPotentialConstantExpression() && + DiagDecl->isConstexpr()) { + return false; + } + // Implicitly constexpr. if (F->isLambdaStaticInvoker()) return true; @@ -939,7 +949,7 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { // Bail out if the function declaration itself is invalid. We will // have produced a relevant diagnostic while parsing it, so just // note the problematic sub-expression. - if (F->getDecl()->isInvalidDecl()) + if (DiagDecl->isInvalidDecl()) return Invalid(S, OpPC); // Diagnose failed assertions specially. @@ -957,64 +967,61 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { } } - if (S.getLangOpts().CPlusPlus11) { - const FunctionDecl *DiagDecl = F->getDecl(); - - // Invalid decls have been diagnosed before. - if (DiagDecl->isInvalidDecl()) - return false; + if (!S.getLangOpts().CPlusPlus11) { + S.FFDiag(S.Current->getLocation(OpPC), + diag::note_invalid_subexpr_in_const_expr); + return false; + } - // If this function is not constexpr because it is an inherited - // non-constexpr constructor, diagnose that directly. - const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl); - if (CD && CD->isInheritingConstructor()) { - const auto *Inherited = CD->getInheritedConstructor().getConstructor(); - if (!Inherited->isConstexpr()) - DiagDecl = CD = Inherited; - } + // Invalid decls have been diagnosed before. + if (DiagDecl->isInvalidDecl()) + return false; - // Silently reject constructors of invalid classes. The invalid class - // has been rejected elsewhere before. - if (CD && CD->getParent()->isInvalidDecl()) - return false; + // If this function is not constexpr because it is an inherited + // non-constexpr constructor, diagnose that directly. + const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl); + if (CD && CD->isInheritingConstructor()) { + const auto *Inherited = CD->getInheritedConstructor().getConstructor(); + if (!Inherited->isConstexpr()) + DiagDecl = CD = Inherited; + } - // FIXME: If DiagDecl is an implicitly-declared special member function - // or an inheriting constructor, we should be much more explicit about why - // it's not constexpr. - if (CD && CD->isInheritingConstructor()) { - S.FFDiag(S.Current->getLocation(OpPC), - diag::note_constexpr_invalid_inhctor, 1) - << CD->getInheritedConstructor().getConstructor()->getParent(); - S.Note(DiagDecl->getLocation(), diag::note_declared_at); - } else { - // Don't emit anything if the function isn't defined and we're checking - // for a constant expression. It might be defined at the point we're - // actually calling it. - bool IsExtern = DiagDecl->getStorageClass() == SC_Extern; - bool IsDefined = F->isDefined(); - if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() && - S.checkingPotentialConstantExpression()) - return false; + // Silently reject constructors of invalid classes. The invalid class + // has been rejected elsewhere before. + if (CD && CD->getParent()->isInvalidDecl()) + return false; - // If the declaration is defined, declared 'constexpr' _and_ has a body, - // the below diagnostic doesn't add anything useful. - if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && - DiagDecl->hasBody()) - return false; + // FIXME: If DiagDecl is an implicitly-declared special member function + // or an inheriting constructor, we should be much more explicit about why + // it's not constexpr. + if (CD && CD->isInheritingConstructor()) { + S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_invalid_inhctor, + 1) + << CD->getInheritedConstructor().getConstructor()->getParent(); + S.Note(DiagDecl->getLocation(), diag::note_declared_at); + } else { + // Don't emit anything if the function isn't defined and we're checking + // for a constant expression. It might be defined at the point we're + // actually calling it. + bool IsExtern = DiagDecl->getStorageClass() == SC_Extern; + bool IsDefined = F->isDefined(); + if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() && + S.checkingPotentialConstantExpression()) + return false; - S.FFDiag(S.Current->getLocation(OpPC), - diag::note_constexpr_invalid_function, 1) - << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + // If the declaration is defined, declared 'constexpr' _and_ has a body, + // the below diagnostic doesn't add anything useful. + if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && DiagDecl->hasBody()) + return false; - if (DiagDecl->getDefinition()) - S.Note(DiagDecl->getDefinition()->getLocation(), - diag::note_declared_at); - else - S.Note(DiagDecl->getLocation(), diag::note_declared_at); - } - } else { S.FFDiag(S.Current->getLocation(OpPC), - diag::note_invalid_subexpr_in_const_expr); + diag::note_constexpr_invalid_function, 1) + << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + + if (DiagDecl->getDefinition()) + S.Note(DiagDecl->getDefinition()->getLocation(), diag::note_declared_at); + else + S.Note(DiagDecl->getLocation(), diag::note_declared_at); } return false; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 89f6fbe..5ab9c8e 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -75,7 +75,8 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B); bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B); /// Checks if a value can be stored in a block. -bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); +bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + bool WillBeActivated = false); /// Checks if a value can be initialized. bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); @@ -1977,13 +1978,12 @@ bool StoreActivate(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } - - if (!CheckStore(S, OpPC, Ptr)) - return false; Ptr.deref<T>() = Value; return true; } @@ -1993,12 +1993,12 @@ bool StoreActivatePop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } - if (!CheckStore(S, OpPC, Ptr)) - return false; Ptr.deref<T>() = Value; return true; } @@ -2007,7 +2007,8 @@ template <PrimType Name, class T = typename PrimConv<Name>::T> bool StoreBitField(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr)) + + if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) return false; if (Ptr.canBeInitialized()) Ptr.initialize(); @@ -2037,12 +2038,13 @@ template <PrimType Name, class T = typename PrimConv<Name>::T> bool StoreBitFieldActivate(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.peek<Pointer>(); + + if (!CheckStore(S, OpPC, Ptr, /*WilLBeActivated=*/true)) + return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } - if (!CheckStore(S, OpPC, Ptr)) - return false; if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); else @@ -2055,12 +2057,12 @@ bool StoreBitFieldActivatePop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (!CheckStore(S, OpPC, Ptr, /*WillBeActivated=*/true)) + return false; if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } - if (!CheckStore(S, OpPC, Ptr)) - return false; if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); else @@ -2281,7 +2283,7 @@ std::optional<Pointer> OffsetHelper(InterpState &S, CodePtr OpPC, } } - if (Invalid && S.getLangOpts().CPlusPlus) + if (Invalid && (S.getLangOpts().CPlusPlus || Ptr.inArray())) return std::nullopt; // Offset is valid - compute it on unsigned. diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 839e84f..8f23001 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -749,7 +749,7 @@ static bool interp__builtin_overflowop(InterpState &S, CodePtr OpPC, const CallExpr *Call, unsigned BuiltinOp) { const Pointer &ResultPtr = S.Stk.pop<Pointer>(); - if (ResultPtr.isDummy()) + if (ResultPtr.isDummy() || !ResultPtr.isBlockPointer()) return false; PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType()); diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp index 90551c2..b42bfa3 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp @@ -177,6 +177,41 @@ static auto isPointerComparisonOperatorCall(std::string operator_name) { pointee(anyOf(statusOrType(), statusType()))))))); } +// The nullPointerConstant in the two matchers below is to support +// absl::StatusOr<void*> X = nullptr. +// nullptr does not match the bound type. +// TODO: be less restrictive around convertible types in general. +static auto isStatusOrValueAssignmentCall() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxOperatorCallExpr( + hasOverloadedOperatorName("="), + callee(cxxMethodDecl(ofClass(statusOrClass()))), + hasArgument(1, anyOf(hasType(hasUnqualifiedDesugaredType( + type(equalsBoundNode("T")))), + nullPointerConstant()))); +} + +static auto isStatusOrValueConstructor() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxConstructExpr( + hasType(statusOrType()), + hasArgument(0, + anyOf(hasType(hasCanonicalType(type(equalsBoundNode("T")))), + nullPointerConstant(), + hasType(namedDecl(hasAnyName("absl::in_place_t", + "std::in_place_t")))))); +} + +static auto isStatusOrConstructor() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxConstructExpr(hasType(statusOrType())); +} + +static auto isStatusConstructor() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxConstructExpr(hasType(statusType())); +} + static auto buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) { return CFGMatchSwitchBuilder<const Environment, @@ -528,6 +563,46 @@ static void transferEmplaceCall(const CXXMemberCallExpr *Expr, State.Env.assume(OkVal.formula()); } +static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + assert(Expr->getNumArgs() > 1); + + auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0)); + if (StatusOrLoc == nullptr) + return; + + auto &OkVal = initializeStatusOr(*StatusOrLoc, State.Env); + State.Env.assume(OkVal.formula()); +} + +static void transferValueConstructor(const CXXConstructExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + auto &OkVal = + initializeStatusOr(State.Env.getResultObjectLocation(*Expr), State.Env); + State.Env.assume(OkVal.formula()); +} + +static void transferStatusOrConstructor(const CXXConstructExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(*Expr); + RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc); + + if (State.Env.getValue(locForOk(StatusLoc)) == nullptr) + initializeStatusOr(StatusOrLoc, State.Env); +} + +static void transferStatusConstructor(const CXXConstructExpr *Expr, + const MatchFinder::MatchResult &, + LatticeTransferState &State) { + RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(*Expr); + + if (State.Env.getValue(locForOk(StatusLoc)) == nullptr) + initializeStatus(StatusLoc, State.Env); +} + CFGMatchSwitch<LatticeTransferState> buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder<LatticeTransferState> Builder) { @@ -573,6 +648,20 @@ buildTransferMatchSwitch(ASTContext &Ctx, .CaseOfCFGStmt<CallExpr>(isNotOkStatusCall(), transferNotOkStatusCall) .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("emplace"), transferEmplaceCall) + .CaseOfCFGStmt<CXXOperatorCallExpr>(isStatusOrValueAssignmentCall(), + transferValueAssignmentCall) + .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrValueConstructor(), + transferValueConstructor) + // N.B. These need to come after all other CXXConstructExpr. + // These are there to make sure that every Status and StatusOr object + // have their ok boolean initialized when constructed. If we were to + // lazily initialize them when we first access them, we can produce + // false positives if that first access is in a control flow statement. + // You can comment out these two constructors and see tests fail. + .CaseOfCFGStmt<CXXConstructExpr>(isStatusOrConstructor(), + transferStatusOrConstructor) + .CaseOfCFGStmt<CXXConstructExpr>(isStatusConstructor(), + transferStatusConstructor) .Build(); } diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 468c930..aefc262 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -713,14 +713,16 @@ static void addSanitizers(const Triple &TargetTriple, ThinOrFullLTOPhase) { if (CodeGenOpts.hasSanitizeCoverage()) { auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); - MPM.addPass(SanitizerCoveragePass( - SancovOpts, CodeGenOpts.SanitizeCoverageAllowlistFiles, - CodeGenOpts.SanitizeCoverageIgnorelistFiles)); + MPM.addPass( + SanitizerCoveragePass(SancovOpts, PB.getVirtualFileSystemPtr(), + CodeGenOpts.SanitizeCoverageAllowlistFiles, + CodeGenOpts.SanitizeCoverageIgnorelistFiles)); } if (CodeGenOpts.hasSanitizeBinaryMetadata()) { MPM.addPass(SanitizerBinaryMetadataPass( getSanitizerBinaryMetadataOptions(CodeGenOpts), + PB.getVirtualFileSystemPtr(), CodeGenOpts.SanitizeMetadataIgnorelistFiles)); } diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index a56fa41..88bce18 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -945,6 +945,13 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, assert(false && "Unexpected action class for Flang tool."); } + // We support some options that are invalid for Fortran and have no effect. + // These are solely for compatibility with other compilers. Emit a warning if + // any such options are provided, then proceed normally. + for (options::ID Opt : {options::OPT_fbuiltin, options::OPT_fno_builtin}) + if (const Arg *A = Args.getLastArg(Opt)) + D.Diag(diag::warn_drv_invalid_argument_for_flang) << A->getSpelling(); + const InputInfo &Input = Inputs[0]; types::ID InputType = Input.getType(); diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 65fc65e..f24b8ab 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -288,6 +288,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, ArrayRef<unsigned> Matches, SmallVector<WhitespaceManager::Change, 16> &Changes) { int Shift = 0; + // Set when the shift is applied anywhere in the line. Cleared when the line + // ends. + bool LineShifted = false; // ScopeStack keeps track of the current scope depth. It contains the levels // of at most 2 scopes. The first one is the one that the matched token is @@ -339,8 +342,11 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, Changes[i - 1].Tok->is(tok::string_literal); bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral; - if (CurrentChange.NewlinesBefore > 0 && !SkipMatchCheck) - Shift = 0; + if (CurrentChange.NewlinesBefore > 0) { + LineShifted = false; + if (!SkipMatchCheck) + Shift = 0; + } // If this is the first matching token to be aligned, remember by how many // spaces it has to be shifted, so the rest of the changes on the line are @@ -349,7 +355,6 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) - CurrentChange.StartOfTokenColumn; ScopeStack = {CurrentChange.indentAndNestingLevel()}; - CurrentChange.Spaces += Shift; } if (Shift == 0) @@ -358,8 +363,10 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, // This is for lines that are split across multiple lines, as mentioned in // the ScopeStack comment. The stack size being 1 means that the token is // not in a scope that should not move. - if (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 && - (ContinuedStringLiteral || InsideNestedScope)) { + if ((!Matches.empty() && Matches[0] == i) || + (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 && + (ContinuedStringLiteral || InsideNestedScope))) { + LineShifted = true; CurrentChange.Spaces += Shift; } @@ -369,9 +376,11 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, static_cast<int>(Changes[i].Tok->SpacesRequiredBefore) || CurrentChange.Tok->is(tok::eof)); - CurrentChange.StartOfTokenColumn += Shift; - if (i + 1 != Changes.size()) - Changes[i + 1].PreviousEndOfTokenColumn += Shift; + if (LineShifted) { + CurrentChange.StartOfTokenColumn += Shift; + if (i + 1 != Changes.size()) + Changes[i + 1].PreviousEndOfTokenColumn += Shift; + } // If PointerAlignment is PAS_Right, keep *s or &s next to the token, // except if the token is equal, then a space is needed. diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index 1c7c832d..ab37394 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -850,8 +850,10 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { assert((BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldrexd || BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == ARM::BI__builtin_arm_strex || + BuiltinID == ARM::BI__builtin_arm_strexd || BuiltinID == ARM::BI__builtin_arm_stlex || BuiltinID == AArch64::BI__builtin_arm_ldrex || BuiltinID == AArch64::BI__builtin_arm_ldaex || @@ -859,9 +861,12 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI, BuiltinID == AArch64::BI__builtin_arm_stlex) && "unexpected ARM builtin"); bool IsLdrex = BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldrexd || BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == AArch64::BI__builtin_arm_ldrex || BuiltinID == AArch64::BI__builtin_arm_ldaex; + bool IsDoubleWord = BuiltinID == ARM::BI__builtin_arm_ldrexd || + BuiltinID == ARM::BI__builtin_arm_strexd; ASTContext &Context = getASTContext(); DeclRefExpr *DRE = @@ -928,6 +933,11 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI, if (!TI.getTriple().isAArch64()) { unsigned Mask = TI.getARMLDREXMask(); unsigned Bits = Context.getTypeSize(ValType); + if (IsDoubleWord) { + // Explicit request for ldrexd/strexd means only double word sizes + // supported if the target supports them. + Mask &= TargetInfo::ARM_LDREX_D; + } bool Supported = (llvm::isPowerOf2_64(Bits)) && Bits >= 8 && (Mask & (Bits / 8)); @@ -968,8 +978,11 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI, } } } else { + bool EmitDoubleWordDiagnostic = + IsDoubleWord && !Mask && TI.getARMLDREXMask(); Diag(DRE->getBeginLoc(), diag::err_atomic_exclusive_builtin_pointer_size_none) + << (EmitDoubleWordDiagnostic ? 1 : 0) << PointerArg->getSourceRange(); } } @@ -1013,8 +1026,10 @@ bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { if (BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldrexd || BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == ARM::BI__builtin_arm_strex || + BuiltinID == ARM::BI__builtin_arm_strexd || BuiltinID == ARM::BI__builtin_arm_stlex) { return CheckARMBuiltinExclusiveCall(TI, BuiltinID, TheCall); } |
