diff --git a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -52,6 +52,7 @@ } void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { + // FIXME: this does not consider NoUnwindAttr, NoThrowAttr. Finder->addMatcher( functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(), cxxConstructorDecl(isMoveConstructor()), diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -115,6 +115,8 @@ if (CallStack.count(Func)) return ExceptionInfo::createNonThrowing(); + // FIXME: this does not deal with NoUnwind/NoThrow attributes. + if (const Stmt *Body = Func->getBody()) { CallStack.insert(Func); ExceptionInfo Result = @@ -201,6 +203,7 @@ } Results.merge(Uncaught); } else if (const auto *Call = dyn_cast(St)) { + // FIXME: this does not deal with stmt NoUnwind attribute. if (const FunctionDecl *Func = Call->getDirectCallee()) { ExceptionInfo Excs = throwsException(Func, CallStack); Results.merge(Excs); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp @@ -26,11 +26,43 @@ } }; +void thrower(int n) { + throw n; +} + void throwing_noexcept() noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_noexcept' which should not throw exceptions throw 1; } +void __attribute__((nothrow)) throwing_attribute_nothrow() { + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: an exception may be thrown in function 'throwing_attribute_nothrow' which should not throw exceptions + throw 1; +} + +void __attribute__((nounwind)) throwing_attribute_nounwind() { + // FIXME: should be diagnosed. + throw 1; +} + +// FIXME: false-positives. +void non_throwing_call_of_noexcept() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'non_throwing_call_of_noexcept' which should not throw exceptions + throwing_noexcept(); +} +void non_throwing_call_of_attribute_nothrow() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'non_throwing_call_of_attribute_nothrow' which should not throw exceptions + throwing_attribute_nothrow(); +} +void non_throwing_call_of_attribute_nounwind() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'non_throwing_call_of_attribute_nounwind' which should not throw exceptions + throwing_attribute_nounwind(); +} +void non_throwing_call_of_throwing() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'non_throwing_call_of_throwing' which should not throw exceptions + [[clang::nounwind]] thrower(1); +} + void throwing_throw_nothing() throw() { // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throwing_throw_nothing' which should not throw exceptions throw 1; @@ -263,10 +295,6 @@ throw ignored1(); } -void thrower(int n) { - throw n; -} - int directly_recursive(int n) noexcept { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'directly_recursive' which should not throw exceptions if (n == 0) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -348,6 +348,9 @@ - Fix issue that the standard C++ modules importer will call global constructor/destructor for the global varaibles in the importing modules. This fixes `Issue 59765 `_ +- ``__attribute__((const))``/``__attribute__((pure))`` are no longer defined as + exhibiting UB if an exception unwinds out a function marked with such + an attribute. This matches GCC behavior. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,6 +583,17 @@ memory placement. It emits a warning if something in the code provably prevents an instance from a read-only memory placement. +- ``__attribute__((const))``/``__attribute__((pure))`` are no longer defined as + exhibiting UB if an exception unwinds out a function marked with such + an attribute. This matches GCC behavior. + +- Function ``__attribute__((nounwind))`` and function/statement + ``[[clang::nounwind]]`` attribute has been created. No exceptions shall + unwind from functions or calls declared with this attribute. If an exception + does unwind, the behaviour is undefined. This is meant as an optimization + hint, because all other attributes define such an exception escape + as causing immediate program termination. + Windows Support --------------- - For the MinGW driver, added the options ``-mguard=none``, ``-mguard=cf`` and diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1104,7 +1104,7 @@ def Const : InheritableAttr { let Spellings = [GCC<"const">, GCC<"__const">]; - let Documentation = [Undocumented]; + let Documentation = [ConstDocs]; let SimpleHandler = 1; } @@ -2391,7 +2391,7 @@ def Pure : InheritableAttr { let Spellings = [GCC<"pure">]; - let Documentation = [Undocumented]; + let Documentation = [PureDocs]; let SimpleHandler = 1; } @@ -4122,8 +4122,18 @@ let Subjects = SubjectList<[Function]>; let Documentation = [FunctionReturnThunksDocs]; } + def ReadOnlyPlacement : InheritableAttr { let Spellings = [Clang<"enforce_read_only_placement">]; let Subjects = SubjectList<[Record]>; let Documentation = [ReadOnlyPlacementDocs]; } + +def NoUnwind : DeclOrStmtAttr { + let Spellings = [Clang<"nounwind", /*allowInC =*/0>]; + let Accessors = [Accessor<"isClangNoUnwind", [CXX11<"clang", "nounwind">]>]; + let Documentation = [NoUnwindDocs]; + let Subjects = SubjectList<[Function, Stmt], WarnDiag, + "functions and statements">; + let SimpleHandler = 1; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -535,20 +535,76 @@ }]; } +def PureDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +A function declared with the ``pure`` attribute guarantees it does not have +infinite loops (i.e. they must either terminate or return to the caller, +be it either via a normal return, or by throwing an exception and unwinding +or exibiting UB), and will not cause any observable side-effects other than +returning a value. They may read memory, but can not modify memory in a way +that would either affect observable state or their outcome on subsequent runs. + +Note: this attribute does not define any behaviour regarding exceptions, +so functions annotated with this attribute are allowed to throw exceptions, +and doing so is neither UB nor will cause immediate program termination, +but rather the exception will successfully unwind into function's caller. + }]; +} + +def ConstDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +A stronger version of ``pure`` attribute. + +A function declared with ``const`` attribute guarantees it does not not have +infinite loops (i.e. they must either terminate or return to the caller, +be it either via a normal return, or by throwing an exception and unwinding +or exibiting UB), and will not cause any observable side-effects other than +returning a value. They cannot write to memory, unlike functions declared +with ``pure`` attribute, but may still read memory that is immutable between +invocations of the function. + +Note: this attribute does not define any behaviour regarding exceptions, +so functions annotated with this attribute are allowed to throw exceptions, +and doing so is neither UB nor will cause immediate program termination, +but rather the exception will successfully unwind into function's caller. + }]; +} + +def NoUnwindDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +A function declared with ``nounwind`` attribute +(avaliable spellings: ``__attribute__((nounwind))`` and ``[[clang::nounwind]]``) +shall not unwind to its caller. + +If a statement is marked with a ``[[clang::nounwind]]`` attribute +and contains call expressions, those call expressions inside the statement +shall not unwind to the caller. + +Compiler will generate a diagnostic for a function / call expression declared +as non-unwinding, that appears to be capable of unwinding to the caller. + +If an exception does unwind out of a function / call expression declared as +non-unwinding, the behaviour is undefined. + }]; +} + def NoMergeDocs : Documentation { let Category = DocCatStmt; let Content = [{ If a statement is marked ``nomerge`` and contains call expressions, those call -expressions inside the statement will not be merged during optimization. This +expressions inside the statement will not be merged during optimization. This attribute can be used to prevent the optimizer from obscuring the source location of certain calls. For example, it will prevent tail merging otherwise identical code sequences that raise an exception or terminate the program. Tail merging normally reduces the precision of source location information, making stack traces less useful for debugging. This attribute gives the user control -over the tradeoff between code size and debug information precision. +over the tradeoff between code size and debug information precision. -``nomerge`` attribute can also be used as function attribute to prevent all -calls to the specified function from merging. It has no effect on indirect +``nomerge`` attribute can also be used as function attribute to prevent all +calls to the specified function from merging. It has no effect on indirect calls. }]; } @@ -1581,7 +1637,7 @@ ``watchos`` Apple's watchOS operating system. The minimum deployment target is specified by the ``-mwatchos-version-min=*version*`` command-line argument. - + ``driverkit`` Apple's DriverKit userspace kernel extensions. The minimum deployment target is specified as part of the triple. @@ -3883,7 +3939,7 @@ with pointers in the C family of languages. The various nullability attributes indicate whether a particular pointer can be null or not, which makes APIs more expressive and can help static analysis tools identify bugs involving null -pointers. Clang supports several kinds of nullability attributes: the +pointers. Clang supports several kinds of nullability attributes: the ``nonnull`` and ``returns_nonnull`` attributes indicate which function or method parameters and result types can never be null, while nullability type qualifiers indicate which pointer types can be null (``_Nullable``) or cannot @@ -4059,7 +4115,7 @@ The ``returns_nonnull`` attribute implies that returning a null pointer is undefined behavior, which the optimizer may take advantage of. The ``_Nonnull`` type qualifier indicates that a pointer cannot be null in a more general manner -(because it is part of the type system) and does not imply undefined behavior, +(because it is part of the type system) and does not imply undefined behavior, making it more widely applicable }]; } @@ -4598,6 +4654,9 @@ does not throw an exception. This prevents exception-unwinding. This attribute is particularly useful on functions in the C Standard Library that are guaranteed to not throw an exception. + +If an exception does unwind out of a function marked with this attribute, +the behaviour matches that of ``noexcept`` -- program is immediately terminated. }]; } @@ -6008,15 +6067,15 @@ let Content = [{ Code can indicate CFG checks are not wanted with the ``__declspec(guard(nocf))`` attribute. This directs the compiler to not insert any CFG checks for the entire -function. This approach is typically used only sparingly in specific situations -where the programmer has manually inserted "CFG-equivalent" protection. The -programmer knows that they are calling through some read-only function table -whose address is obtained through read-only memory references and for which the -index is masked to the function table limit. This approach may also be applied -to small wrapper functions that are not inlined and that do nothing more than -make a call through a function pointer. Since incorrect usage of this directive -can compromise the security of CFG, the programmer must be very careful using -the directive. Typically, this usage is limited to very small functions that +function. This approach is typically used only sparingly in specific situations +where the programmer has manually inserted "CFG-equivalent" protection. The +programmer knows that they are calling through some read-only function table +whose address is obtained through read-only memory references and for which the +index is masked to the function table limit. This approach may also be applied +to small wrapper functions that are not inlined and that do nothing more than +make a call through a function pointer. Since incorrect usage of this directive +can compromise the security of CFG, the programmer must be very careful using +the directive. Typically, this usage is limited to very small functions that only call one function. `Control Flow Guard documentation ` diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h --- a/clang/include/clang/Basic/Builtins.h +++ b/clang/include/clang/Basic/Builtins.h @@ -111,7 +111,7 @@ } /// Return true if we know this builtin never throws an exception. - bool isNoThrow(unsigned ID) const { + bool isNoUnwind(unsigned ID) const { return strchr(getRecord(ID).Attributes, 'n') != nullptr; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -11480,7 +11480,7 @@ FunctionProtoType::ExtProtoInfo EPI; EPI.ExtInfo = EI; EPI.Variadic = Variadic; - if (getLangOpts().CPlusPlus && BuiltinInfo.isNoThrow(Id)) + if (getLangOpts().CPlusPlus && BuiltinInfo.isNoUnwind(Id)) EPI.ExceptionSpec.Type = getLangOpts().CPlusPlus11 ? EST_BasicNoexcept : EST_DynamicNone; diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -2722,7 +2722,7 @@ if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context)) NoReturn = true; - if (FD->hasAttr()) + if (FD->hasAttr() || FD->hasAttr()) AddEHEdge = false; if (FD->getBuiltinID() == Builtin::BI__builtin_object_size || FD->getBuiltinID() == Builtin::BI__builtin_dynamic_object_size) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2222,7 +2222,7 @@ getContext().BuiltinInfo.isConstWithoutErrnoAndExceptions(BuiltinID); bool ConstWithoutExceptions = getContext().BuiltinInfo.isConstWithoutExceptions(BuiltinID); - if (FD->hasAttr() || + if ((FD->hasAttr() && FD->hasAttr()) || ((ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) && (!ConstWithoutErrnoAndExceptions || (!getLangOpts().MathErrno)))) { switch (BuiltinIDIfNoAsmLabel) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2168,7 +2168,8 @@ if (TargetDecl) { if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::ReturnsTwice); - if (TargetDecl->hasAttr()) + if (TargetDecl->hasAttr() || + TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::NoReturn); @@ -2206,16 +2207,16 @@ FuncAttrs.addAttribute(llvm::Attribute::NoMerge); } - // 'const', 'pure' and 'noalias' attributed functions are also nounwind. + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); + if (TargetDecl->hasAttr()) { FuncAttrs.addMemoryAttr(llvm::MemoryEffects::none()); - FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); // gcc specifies that 'const' functions have greater restrictions than // 'pure' functions, so they also cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { FuncAttrs.addMemoryAttr(llvm::MemoryEffects::readOnly()); - FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); // gcc specifies that 'pure' functions cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { @@ -5329,6 +5330,10 @@ // All calls within a strictfp function are marked strictfp Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::StrictFP); + // Add call-site nounwind attribute if exists. + if (InNoUnwindAttributedStmt) + Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoUnwind); + // Add call-site nomerge attribute if exists. if (InNoMergeAttributedStmt) Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoMerge); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -695,6 +695,7 @@ } void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) { + bool nounwind = false; bool nomerge = false; bool noinline = false; bool alwaysinline = false; @@ -704,6 +705,9 @@ switch (A->getKind()) { default: break; + case attr::NoUnwind: + nounwind = true; + break; case attr::NoMerge: nomerge = true; break; @@ -720,6 +724,7 @@ break; } } + SaveAndRestore save_nounwind(InNoUnwindAttributedStmt, nounwind); SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge); SaveAndRestore save_noinline(InNoInlineAttributedStmt, noinline); SaveAndRestore save_alwaysinline(InAlwaysInlineAttributedStmt, alwaysinline); @@ -2268,7 +2273,8 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect, bool HasUnwindClobber, bool ReadOnly, - bool ReadNone, bool NoMerge, const AsmStmt &S, + bool ReadNone, bool NoMerge, bool NoUnwind, + const AsmStmt &S, const std::vector &ResultRegTypes, const std::vector &ArgElemTypes, CodeGenFunction &CGF, @@ -2278,6 +2284,8 @@ if (NoMerge) Result.addFnAttr(llvm::Attribute::NoMerge); + if (NoUnwind) + Result.addFnAttr(llvm::Attribute::NoUnwind); // Attach readnone and readonly attributes. if (!HasSideEffect) { if (ReadNone) @@ -2725,19 +2733,21 @@ Builder.CreateCallBr(IA, Fallthrough, Transfer, Args); EmitBlock(Fallthrough); UpdateAsmCallInst(cast(*Result), HasSideEffect, false, - ReadOnly, ReadNone, InNoMergeAttributedStmt, S, - ResultRegTypes, ArgElemTypes, *this, RegResults); + ReadOnly, ReadNone, InNoMergeAttributedStmt, + InNoUnwindAttributedStmt, S, ResultRegTypes, ArgElemTypes, + *this, RegResults); } else if (HasUnwindClobber) { llvm::CallBase *Result = EmitCallOrInvoke(IA, Args, ""); UpdateAsmCallInst(*Result, HasSideEffect, true, ReadOnly, ReadNone, - InNoMergeAttributedStmt, S, ResultRegTypes, ArgElemTypes, - *this, RegResults); + InNoMergeAttributedStmt, InNoUnwindAttributedStmt, S, + ResultRegTypes, ArgElemTypes, *this, RegResults); } else { llvm::CallInst *Result = Builder.CreateCall(IA, Args, getBundlesForFunclet(IA)); UpdateAsmCallInst(cast(*Result), HasSideEffect, false, - ReadOnly, ReadNone, InNoMergeAttributedStmt, S, - ResultRegTypes, ArgElemTypes, *this, RegResults); + ReadOnly, ReadNone, InNoMergeAttributedStmt, + InNoUnwindAttributedStmt, S, ResultRegTypes, ArgElemTypes, + *this, RegResults); } assert(RegResults.size() == ResultRegTypes.size()); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -549,6 +549,9 @@ /// region. bool IsInPreservedAIRegion = false; + /// True if the current statement has nounwind attribute. + bool InNoUnwindAttributedStmt = false; + /// True if the current statement has nomerge attribute. bool InNoMergeAttributedStmt = false; diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -371,7 +371,8 @@ static bool isNoexcept(const FunctionDecl *FD) { const auto *FPT = FD->getType()->castAs(); - if (FPT->isNothrow() || FD->hasAttr()) + if (FPT->isNothrow() || FD->hasAttr() || + FD->hasAttr()) return true; return false; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16082,19 +16082,23 @@ Context.BuiltinInfo.isConstWithoutErrnoAndExceptions(BuiltinID); bool ConstWithoutExceptions = Context.BuiltinInfo.isConstWithoutExceptions(BuiltinID); - if (!FD->hasAttr() && + if ((!FD->hasAttr() || !FD->hasAttr()) && (ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) && (!ConstWithoutErrnoAndExceptions || (!getLangOpts().MathErrno && NoExceptions)) && - (!ConstWithoutExceptions || NoExceptions)) - FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); + (!ConstWithoutExceptions || NoExceptions)) { + if (!FD->hasAttr()) + FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); + if (!FD->hasAttr()) + FD->addAttr(NoUnwindAttr::CreateImplicit(Context, FD->getLocation())); + } // We make "fma" on GNU or Windows const because we know it does not set // errno in those environments even though it could set errno based on the // C standard. const llvm::Triple &Trip = Context.getTargetInfo().getTriple(); if ((Trip.isGNUEnvironment() || Trip.isOSMSVCRT()) && - !FD->hasAttr()) { + (!FD->hasAttr() || !FD->hasAttr())) { switch (BuiltinID) { case Builtin::BI__builtin_fma: case Builtin::BI__builtin_fmaf: @@ -16102,8 +16106,11 @@ case Builtin::BIfma: case Builtin::BIfmaf: case Builtin::BIfmal: - FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); - break; + if (!FD->hasAttr()) + FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); + if (!FD->hasAttr()) + FD->addAttr(NoUnwindAttr::CreateImplicit(Context, FD->getLocation())); + break; default: break; } @@ -16113,8 +16120,9 @@ !FD->hasAttr()) FD->addAttr(ReturnsTwiceAttr::CreateImplicit(Context, FD->getLocation())); - if (Context.BuiltinInfo.isNoThrow(BuiltinID) && !FD->hasAttr()) - FD->addAttr(NoThrowAttr::CreateImplicit(Context, FD->getLocation())); + if (Context.BuiltinInfo.isNoUnwind(BuiltinID) && + !FD->hasAttr()) + FD->addAttr(NoUnwindAttr::CreateImplicit(Context, FD->getLocation())); if (Context.BuiltinInfo.isPure(BuiltinID) && !FD->hasAttr()) FD->addAttr(PureAttr::CreateImplicit(Context, FD->getLocation())); if (Context.BuiltinInfo.isConst(BuiltinID) && !FD->hasAttr()) @@ -16169,6 +16177,7 @@ // If C++ exceptions are enabled but we are told extern "C" functions cannot // throw, add an implicit nothrow attribute to any extern "C" function we come // across. + // FIXME: do we want UB or program termination on unwind here? if (getLangOpts().CXXExceptions && getLangOpts().ExternCNoUnwind && FD->isExternC() && !FD->hasAttr()) { const auto *FPT = FD->getType()->getAs(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -189,6 +189,7 @@ if (ComputedEST == EST_None) return; + // FIXME: what about NoUnwindAttr? if (EST == EST_None && Method->hasAttr()) EST = EST_BasicNoexcept; diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1006,9 +1006,10 @@ CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D, SourceLocation Loc) { - // As an extension, we assume that __attribute__((nothrow)) functions don't - // throw. - if (D && isa(D) && D->hasAttr()) + // As an extension, we assume that __attribute__((nounwind)) and + // __attribute__((nothrow)) functions don't throw. + if (D && isa(D) && + (D->hasAttr() || D->hasAttr())) return CT_Cannot; QualType T; diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -240,6 +240,25 @@ return ::new (S.Context) NoInlineAttr(S.Context, A); } +static Attr *handleNoUnwindAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + NoUnwindAttr NIA(S.Context, A); + if (!NIA.isClangNoUnwind()) { + S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) + << "[[clang::nounwind]]"; + return nullptr; + } + + CallExprFinder CEF(S, St); + if (!CEF.foundCallExpr()) { + S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt) + << A; + return nullptr; + } + + return ::new (S.Context) NoUnwindAttr(S.Context, A); +} + static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { AlwaysInlineAttr AIA(S.Context, A); @@ -483,6 +502,8 @@ return handleNoMergeAttr(S, St, A, Range); case ParsedAttr::AT_NoInline: return handleNoInlineAttr(S, St, A, Range); + case ParsedAttr::AT_NoUnwind: + return handleNoUnwindAttr(S, St, A, Range); case ParsedAttr::AT_MustTail: return handleMustTailAttr(S, St, A, Range); case ParsedAttr::AT_Likely: diff --git a/clang/test/CodeGen/function-attributes.c b/clang/test/CodeGen/function-attributes.c --- a/clang/test/CodeGen/function-attributes.c +++ b/clang/test/CodeGen/function-attributes.c @@ -45,7 +45,7 @@ // FIXME: We should be setting nounwind on calls. // CHECK: call i32 @f10_t() -// CHECK: [[NUW_RN:#[0-9]+]] +// CHECK: [[RN:#[0-9]+]] // CHECK: { int __attribute__((const)) f10_t(void); int f10(void) { return f10_t(); } @@ -57,9 +57,9 @@ return arg ? 0 : f10_t(); } -// CHECK: define{{.*}} void @f13() [[NUW_OS_RN:#[0-9]+]] -void f13(void) __attribute__((pure)) __attribute__((const)); -void f13(void){} +// CHECK: declare{{.*}} void @f13_t() [[OS_RN:#[0-9]+]] +void f13_t(void) __attribute__((pure)) __attribute__((const)); +void f13(void) { return f13_t(); } // [irgen] clang isn't setting the optsize bit on functions @@ -109,11 +109,20 @@ _setjmp(0); } +// CHECK: declare{{.*}} void @f21_t() [[NUW2:#[0-9]+]] +void f21_t(void) __attribute__((nounwind)); +void f21(void) { return f21_t(); } + +// CHECK: declare{{.*}} void @f22_t() [[NUW2:#[0-9]+]] +void f22_t(void) __attribute__((nounwind)); +void f22(void) { return f22_t(); } + // CHECK: attributes [[NUW]] = { nounwind optsize{{.*}} } // CHECK: attributes [[AI]] = { alwaysinline nounwind optsize{{.*}} } -// CHECK: attributes [[NUW_OS_RN]] = { nounwind optsize willreturn memory(none){{.*}} } +// CHECK: attributes [[OS_RN]] = { optsize willreturn memory(none){{.*}} } // CHECK: attributes [[SR]] = { nounwind optsize{{.*}} "stackrealign"{{.*}} } // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} } +// CHECK: attributes [[NUW2]] = { nounwind optsize{{.*}} } // CHECK: attributes [[NR]] = { noreturn optsize } -// CHECK: attributes [[NUW_RN]] = { nounwind optsize willreturn memory(none) } +// CHECK: attributes [[RN]] = { optsize willreturn memory(none) } // CHECK: attributes [[RT_CALL]] = { optsize returns_twice } diff --git a/clang/test/CodeGen/struct-passing.c b/clang/test/CodeGen/struct-passing.c --- a/clang/test/CodeGen/struct-passing.c +++ b/clang/test/CodeGen/struct-passing.c @@ -23,5 +23,5 @@ // CHECK: declare void @f4({{.*}} byval({{.*}}) align 4) // CHECK: declare void @f5({{.*}} byval({{.*}}) align 4) -// CHECK: attributes [[RN]] = { nounwind willreturn memory(none){{.*}} } -// CHECK: attributes [[RO]] = { nounwind willreturn memory(read){{.*}} } +// CHECK: attributes [[RN]] = { willreturn memory(none){{.*}} } +// CHECK: attributes [[RO]] = { willreturn memory(read){{.*}} } diff --git a/clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp b/clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp --- a/clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp +++ b/clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp @@ -1,22 +1,30 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown -fexceptions -emit-llvm %s -o - | FileCheck %s int c(void) __attribute__((const)); int p(void) __attribute__((pure)); +int NoT(void) __attribute__((nothrow)); +int NoU(void) __attribute__((nounwind)); int t(void); // CHECK: define{{.*}} i32 @_Z1fv() [[TF:#[0-9]+]] { int f(void) { - // CHECK: call noundef i32 @_Z1cv() [[NUW_RN_CALL:#[0-9]+]] - // CHECK: call noundef i32 @_Z1pv() [[NUW_RO_CALL:#[0-9]+]] - return c() + p() + t(); + // CHECK: call noundef i32 @_Z1cv() [[RN_CALL:#[0-9]+]] + // CHECK: call noundef i32 @_Z1pv() [[RO_CALL:#[0-9]+]] + // CHECK: call noundef i32 @_Z3NoTv() [[NUW_CALL:#[0-9]+]] + // CHECK: call noundef i32 @_Z3NoUv() [[NUW_CALL]] + return c() + p() + NoT() + NoU() + t(); } -// CHECK: declare noundef i32 @_Z1cv() [[NUW_RN:#[0-9]+]] -// CHECK: declare noundef i32 @_Z1pv() [[NUW_RO:#[0-9]+]] +// CHECK: declare noundef i32 @_Z1cv() [[RN:#[0-9]+]] +// CHECK: declare noundef i32 @_Z1pv() [[RO:#[0-9]+]] +// CHECK: declare noundef i32 @_Z3NoTv() [[NUW:#[0-9]+]] +// CHECK: declare noundef i32 @_Z3NoUv() [[NUW]] // CHECK: declare noundef i32 @_Z1tv() [[TF2:#[0-9]+]] // CHECK: attributes [[TF]] = { {{.*}} } -// CHECK: attributes [[NUW_RN]] = { nounwind willreturn memory(none){{.*}} } -// CHECK: attributes [[NUW_RO]] = { nounwind willreturn memory(read){{.*}} } +// CHECK: attributes [[RN]] = { willreturn memory(none){{.*}} } +// CHECK: attributes [[RO]] = { willreturn memory(read){{.*}} } +// CHECK: attributes [[NUW]] = { nounwind {{.*}} } // CHECK: attributes [[TF2]] = { {{.*}} } -// CHECK: attributes [[NUW_RN_CALL]] = { nounwind willreturn memory(none) } -// CHECK: attributes [[NUW_RO_CALL]] = { nounwind willreturn memory(read) } +// CHECK: attributes [[RN_CALL]] = { willreturn memory(none) } +// CHECK: attributes [[RO_CALL]] = { willreturn memory(read) } +// CHECK: attributes [[NUW_CALL]] = { nounwind } diff --git a/clang/test/CodeGenCXX/exception-escape-RAII-codegen.cpp b/clang/test/CodeGenCXX/exception-escape-RAII-codegen.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/exception-escape-RAII-codegen.cpp @@ -0,0 +1,630 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s + +void __attribute__((nounwind)) callee_with_gcc_nounwind_attr(int line = __builtin_LINE()); +[[clang::nounwind]] void callee_with_clang_nounwind_attr(int line = __builtin_LINE()); +void callee_with_noexcept_attr(int line = __builtin_LINE()) noexcept; +void callee_without_attrs(int line = __builtin_LINE()); + +__attribute__((nounwind)) void caught_an_exception(int line = __builtin_LINE()); + +struct OuterRAII { + __attribute__((nounwind)) OuterRAII(int line = __builtin_LINE()); + __attribute__((nounwind)) ~OuterRAII(); +}; +struct InnerRAII { + __attribute__((nounwind)) InnerRAII(int line = __builtin_LINE()); + __attribute__((nounwind)) ~InnerRAII(); +}; + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z62caller_with_gcc_nounwind_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 34) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 36) #[[ATTR5]] +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 37) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_gcc_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_gcc_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z64caller_with_gcc_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 57) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 59) #[[ATTR5]] +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 60) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_clang_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_clang_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z58caller_with_gcc_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 80) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 82) #[[ATTR5]] +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 83) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_noexcept_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_noexcept_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z53caller_with_gcc_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 141) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 143) #[[ATTR5]] +// CHECK-NEXT: invoke void @_Z20callee_without_attrsi(i32 noundef 144) +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK: invoke.cont: +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[TRY_CONT:%.*]] +// CHECK: lpad: +// CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[CATCH:%.*]] +// CHECK: catch: +// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NEXT: call void @_Z19caught_an_exceptioni(i32 noundef 146) #[[ATTR5]] +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD1:%.*]] +// CHECK: invoke.cont2: +// CHECK-NEXT: br label [[TRY_CONT]] +// CHECK: try.cont: +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: lpad1: +// CHECK-NEXT: [[TMP4:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: cleanup +// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 1 +// CHECK-NEXT: store i32 [[TMP6]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[EH_RESUME:%.*]] +// CHECK: eh.resume: +// CHECK-NEXT: [[EXN3:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN3]], 0 +// CHECK-NEXT: [[LPAD_VAL4:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1 +// CHECK-NEXT: resume { ptr, i32 } [[LPAD_VAL4]] +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_without_attrs() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_without_attrs(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z64caller_with_clang_nounwind_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 164) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 166) #[[ATTR5]] +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 167) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_gcc_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_gcc_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z66caller_with_clang_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 187) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 189) #[[ATTR5]] +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 190) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_clang_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_clang_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_clang_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 210) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 212) #[[ATTR5]] +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 213) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_noexcept_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_noexcept_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z55caller_with_clang_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 271) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 273) #[[ATTR5]] +// CHECK-NEXT: invoke void @_Z20callee_without_attrsi(i32 noundef 274) +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK: invoke.cont: +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[TRY_CONT:%.*]] +// CHECK: lpad: +// CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[CATCH:%.*]] +// CHECK: catch: +// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NEXT: call void @_Z19caught_an_exceptioni(i32 noundef 276) #[[ATTR5]] +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD1:%.*]] +// CHECK: invoke.cont2: +// CHECK-NEXT: br label [[TRY_CONT]] +// CHECK: try.cont: +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: lpad1: +// CHECK-NEXT: [[TMP4:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: cleanup +// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 1 +// CHECK-NEXT: store i32 [[TMP6]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[EH_RESUME:%.*]] +// CHECK: eh.resume: +// CHECK-NEXT: [[EXN3:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN3]], 0 +// CHECK-NEXT: [[LPAD_VAL4:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1 +// CHECK-NEXT: resume { ptr, i32 } [[LPAD_VAL4]] +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_without_attrs() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_without_attrs(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z58caller_with_noexcept_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 294) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 296) #[[ATTR5]] +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 297) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_gcc_nounwind_attr() noexcept { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_gcc_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_noexcept_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 317) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 319) #[[ATTR5]] +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 320) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_clang_nounwind_attr() noexcept { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_clang_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z54caller_with_noexcept_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 340) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 342) #[[ATTR5]] +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 343) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_noexcept_attr() noexcept { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_noexcept_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z49caller_with_noexcept_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 392) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 394) #[[ATTR5]] +// CHECK-NEXT: invoke void @_Z20callee_without_attrsi(i32 noundef 395) +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK: invoke.cont: +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[TRY_CONT:%.*]] +// CHECK: lpad: +// CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[CATCH:%.*]] +// CHECK: catch: +// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NEXT: call void @_Z19caught_an_exceptioni(i32 noundef 397) #[[ATTR5]] +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label [[INVOKE_CONT1:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK: invoke.cont1: +// CHECK-NEXT: br label [[TRY_CONT]] +// CHECK: try.cont: +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: terminate.lpad: +// CHECK-NEXT: [[TMP4:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 0 +// CHECK-NEXT: call void @__clang_call_terminate(ptr [[TMP5]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: unreachable +// +void caller_with_noexcept_attr_of_callee_without_attrs() noexcept { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_without_attrs(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z53caller_without_attrs_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 415) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 417) #[[ATTR5]] +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 418) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_gcc_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_gcc_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z55caller_without_attrs_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 438) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 440) #[[ATTR5]] +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 441) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_clang_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_clang_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z49caller_without_attrs_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 461) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 463) #[[ATTR5]] +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 464) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_noexcept_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_with_noexcept_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline optnone +// CHECK-LABEL: define {{[^@]+}}@_Z44caller_without_attrs_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 522) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 524) #[[ATTR5]] +// CHECK-NEXT: invoke void @_Z20callee_without_attrsi(i32 noundef 525) +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK: invoke.cont: +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[TRY_CONT:%.*]] +// CHECK: lpad: +// CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[CATCH:%.*]] +// CHECK: catch: +// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NEXT: call void @_Z19caught_an_exceptioni(i32 noundef 527) #[[ATTR5]] +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD1:%.*]] +// CHECK: invoke.cont2: +// CHECK-NEXT: br label [[TRY_CONT]] +// CHECK: try.cont: +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: lpad1: +// CHECK-NEXT: [[TMP4:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: cleanup +// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 1 +// CHECK-NEXT: store i32 [[TMP6]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: br label [[EH_RESUME:%.*]] +// CHECK: eh.resume: +// CHECK-NEXT: [[EXN3:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN3]], 0 +// CHECK-NEXT: [[LPAD_VAL4:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1 +// CHECK-NEXT: resume { ptr, i32 } [[LPAD_VAL4]] +// +void caller_without_attrs_of_callee_without_attrs() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + callee_without_attrs(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z68caller_with_clang_stmt_nounwind_att_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 545) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 547) #[[ATTR5]] +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 548) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_att_of_callee_with_gcc_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + [[clang::nounwind]] callee_with_gcc_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z71caller_with_clang_stmt_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 568) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 570) #[[ATTR5]] +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 571) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_with_clang_nounwind_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + [[clang::nounwind]] callee_with_clang_nounwind_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z65caller_with_clang_stmt_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 591) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 593) #[[ATTR5]] +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 594) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_with_noexcept_attr() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + [[clang::nounwind]] callee_with_noexcept_attr(); + } catch (...) { + caught_an_exception(); + } +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_clang_stmt_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUNCTION_SCOPE:%.*]] = alloca [[STRUCT_OUTERRAII:%.*]], align 1 +// CHECK-NEXT: [[TRY_SCOPE:%.*]] = alloca [[STRUCT_INNERRAII:%.*]], align 1 +// CHECK-NEXT: call void @_ZN9OuterRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]], i32 noundef 614) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIIC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]], i32 noundef 616) #[[ATTR5]] +// CHECK-NEXT: call void @_Z20callee_without_attrsi(i32 noundef 617) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9InnerRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[TRY_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: call void @_ZN9OuterRAIID1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[FUNCTION_SCOPE]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_without_attrs() { + OuterRAII function_scope; + try { + InnerRAII try_scope; + [[clang::nounwind]] callee_without_attrs(); + } catch (...) { + caught_an_exception(); + } +} + +//. +// CHECK: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #1 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #3 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #4 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #5 = { nounwind } +// CHECK: attributes #6 = { noreturn nounwind } diff --git a/clang/test/CodeGenCXX/exception-escape-codegen.cpp b/clang/test/CodeGenCXX/exception-escape-codegen.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/exception-escape-codegen.cpp @@ -0,0 +1,229 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s + +void __attribute__((nounwind)) callee_with_gcc_nounwind_attr(int line = __builtin_LINE()); +[[clang::nounwind]] void callee_with_clang_nounwind_attr(int line = __builtin_LINE()); +void callee_with_noexcept_attr(int line = __builtin_LINE()) noexcept; +void callee_without_attrs(int line = __builtin_LINE()); + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z62caller_with_gcc_nounwind_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 17) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_gcc_nounwind_attr() { + callee_with_gcc_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z64caller_with_gcc_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 27) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_clang_nounwind_attr() { + callee_with_clang_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z58caller_with_gcc_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 37) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_with_noexcept_attr() { + callee_with_noexcept_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z53caller_with_gcc_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z20callee_without_attrsi(i32 noundef 47) +// CHECK-NEXT: ret void +// +void __attribute__((nounwind)) caller_with_gcc_nounwind_attr_of_callee_without_attrs() { + callee_without_attrs(); +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z64caller_with_clang_nounwind_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 58) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_gcc_nounwind_attr() { + callee_with_gcc_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z66caller_with_clang_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 68) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_clang_nounwind_attr() { + callee_with_clang_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_clang_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 78) #[[ATTR5]] +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_with_noexcept_attr() { + callee_with_noexcept_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z55caller_with_clang_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z20callee_without_attrsi(i32 noundef 88) +// CHECK-NEXT: ret void +// +[[clang::nounwind]] void caller_with_clang_nounwind_attr_of_callee_without_attrs() { + callee_without_attrs(); +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z58caller_with_noexcept_attr_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 99) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_gcc_nounwind_attr() noexcept { + callee_with_gcc_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_noexcept_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 109) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_clang_nounwind_attr() noexcept { + callee_with_clang_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z54caller_with_noexcept_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 119) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_noexcept_attr_of_callee_with_noexcept_attr() noexcept { + callee_with_noexcept_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z49caller_with_noexcept_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: invoke void @_Z20callee_without_attrsi(i32 noundef 137) +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK: invoke.cont: +// CHECK-NEXT: ret void +// CHECK: terminate.lpad: +// CHECK-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-NEXT: call void @__clang_call_terminate(ptr [[TMP1]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: unreachable +// +void caller_with_noexcept_attr_of_callee_without_attrs() noexcept { + callee_without_attrs(); +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z53caller_without_attrs_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 148) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_gcc_nounwind_attr() { + callee_with_gcc_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z55caller_without_attrs_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 158) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_clang_nounwind_attr() { + callee_with_clang_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z49caller_without_attrs_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 168) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_with_noexcept_attr() { + callee_with_noexcept_attr(); +} +// CHECK: Function Attrs: mustprogress noinline optnone +// CHECK-LABEL: define {{[^@]+}}@_Z44caller_without_attrs_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z20callee_without_attrsi(i32 noundef 178) +// CHECK-NEXT: ret void +// +void caller_without_attrs_of_callee_without_attrs() { + callee_without_attrs(); +} + +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z68caller_with_clang_stmt_nounwind_att_of_callee_with_gcc_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z29callee_with_gcc_nounwind_attri(i32 noundef 189) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_att_of_callee_with_gcc_nounwind_attr() { + [[clang::nounwind]] callee_with_gcc_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z71caller_with_clang_stmt_nounwind_attr_of_callee_with_clang_nounwind_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z31callee_with_clang_nounwind_attri(i32 noundef 199) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_with_clang_nounwind_attr() { + [[clang::nounwind]] callee_with_clang_nounwind_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z65caller_with_clang_stmt_nounwind_attr_of_callee_with_noexcept_attrv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z25callee_with_noexcept_attri(i32 noundef 209) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_with_noexcept_attr() { + [[clang::nounwind]] callee_with_noexcept_attr(); +} +// CHECK: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-LABEL: define {{[^@]+}}@_Z60caller_with_clang_stmt_nounwind_attr_of_callee_without_attrsv +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_Z20callee_without_attrsi(i32 noundef 219) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_with_clang_stmt_nounwind_attr_of_callee_without_attrs() { + [[clang::nounwind]] callee_without_attrs(); +} + +//. +// CHECK: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #1 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #3 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #4 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #5 = { nounwind } +// CHECK: attributes #6 = { noreturn nounwind } diff --git a/clang/test/CodeGenCXX/pr58798.cpp b/clang/test/CodeGenCXX/pr58798.cpp deleted file mode 100644 --- a/clang/test/CodeGenCXX/pr58798.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --check-attributes -// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) -// CHECK-LABEL: define {{[^@]+}}@_Z54early_caller_of_callee_with_clang_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3:[0-9]+]] -// CHECK-NEXT: ret i32 [[CALL]] -// - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) -// CHECK-LABEL: define {{[^@]+}}@_Z22callee_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 -// CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] -// CHECK: if.then: -// CHECK-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR4:[0-9]+]] -// CHECK-NEXT: store i32 42, ptr [[EXCEPTION]], align 16 -// CHECK-NEXT: call void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR5:[0-9]+]] -// CHECK-NEXT: unreachable -// CHECK: if.end: -// CHECK-NEXT: ret i32 24 -// - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone -// CHECK-LABEL: define {{[^@]+}}@_Z52early_caller_of_callee_with_clang_attr_with_cxx_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1:[0-9]+]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3]] -// CHECK-NEXT: ret i32 [[CALL]] -// - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) -// CHECK-LABEL: define {{[^@]+}}@_Z52early_caller_of_callee_with_cxx_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR4]] -// CHECK-NEXT: ret i32 [[CALL]] -// - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone -// CHECK-LABEL: define {{[^@]+}}@_Z20callee_with_cxx_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1]] personality ptr @__gxx_personality_v0 { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 -// CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] -// CHECK: if.then: -// CHECK-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR4]] -// CHECK-NEXT: store i32 42, ptr [[EXCEPTION]], align 16 -// CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR5]] -// CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD:%.*]] -// CHECK: if.end: -// CHECK-NEXT: ret i32 24 -// CHECK: terminate.lpad: -// CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } -// CHECK-NEXT: catch ptr null -// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0 -// CHECK-NEXT: call void @__clang_call_terminate(ptr [[TMP2]]) #[[ATTR6:[0-9]+]] -// CHECK-NEXT: unreachable -// CHECK: unreachable: -// CHECK-NEXT: unreachable -// - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone -// CHECK-LABEL: define {{[^@]+}}@_Z50early_caller_of_callee_with_cxx_attr_with_cxx_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR4]] -// CHECK-NEXT: ret i32 [[CALL]] -// - - -// Forward declarations: - -__attribute__((pure)) int callee_with_clang_attr(int a); -int callee_with_cxx_attr(int a) noexcept; - -// Calls to forward declarations: - -__attribute__((pure)) int early_caller_of_callee_with_clang_attr_with_clang_attr(int a) { - return callee_with_clang_attr(a); -} - -int early_caller_of_callee_with_clang_attr_with_cxx_attr(int a) noexcept { - return callee_with_clang_attr(a); -} - -__attribute__((pure)) int early_caller_of_callee_with_cxx_attr_with_clang_attr(int a) { - return callee_with_cxx_attr(a); -} - -int early_caller_of_callee_with_cxx_attr_with_cxx_attr(int a) noexcept { - return callee_with_cxx_attr(a); -} - -// Definitions: - -__attribute__((pure)) int callee_with_clang_attr(int a) { - if(a) - throw int(42); - return 24; -} - -int callee_with_cxx_attr(int a) noexcept { - if(a) - throw int(42); - return 24; -} - -// Calls to definitions: - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) -// CHECK-LABEL: define {{[^@]+}}@_Z53late_caller_of_callee_with_clang_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3]] -// CHECK-NEXT: ret i32 [[CALL]] -// -__attribute__((pure)) int late_caller_of_callee_with_clang_attr_with_clang_attr(int a) { - return callee_with_clang_attr(a); -} - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone -// CHECK-LABEL: define {{[^@]+}}@_Z51late_caller_of_callee_with_clang_attr_with_cxx_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3]] -// CHECK-NEXT: ret i32 [[CALL]] -// -int late_caller_of_callee_with_clang_attr_with_cxx_attr(int a) noexcept { - return callee_with_clang_attr(a); -} - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) -// CHECK-LABEL: define {{[^@]+}}@_Z51late_caller_of_callee_with_cxx_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR4]] -// CHECK-NEXT: ret i32 [[CALL]] -// -__attribute__((pure)) int late_caller_of_callee_with_cxx_attr_with_clang_attr(int a) { - return callee_with_cxx_attr(a); -} - -// CHECK: Function Attrs: mustprogress noinline nounwind optnone -// CHECK-LABEL: define {{[^@]+}}@_Z49late_caller_of_callee_with_cxx_attr_with_cxx_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1]] { -// CHECK-NEXT: entry: -// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 -// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 -// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR4]] -// CHECK-NEXT: ret i32 [[CALL]] -// -int late_caller_of_callee_with_cxx_attr_with_cxx_attr(int a) noexcept { - return callee_with_cxx_attr(a); -} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -115,6 +115,7 @@ // CHECK-NEXT: NoStackProtector (SubjectMatchRule_function) // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function) // CHECK-NEXT: NoThrow (SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: NoUnwind (SubjectMatchRule_function) // CHECK-NEXT: NoUwtable (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: NotTailCalled (SubjectMatchRule_function) // CHECK-NEXT: OSConsumed (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/Sema/attr-nounwind.cpp b/clang/test/Sema/attr-nounwind.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Sema/attr-nounwind.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -fexceptions -fcxx-exceptions %s + +int bar(); + +[[clang::nounwind]] void nounwind_fn(void) { } + +void foo() { + [[clang::nounwind]] bar(); + [[clang::nounwind(0)]] bar(); // expected-error {{'nounwind' attribute takes no arguments}} + int x; + [[clang::nounwind]] x = 0; // expected-warning {{'nounwind' attribute is ignored because there exists no call expression inside the statement}} + [[clang::nounwind]] { asm("nop"); } // expected-warning {{'nounwind' attribute is ignored because there exists no call expression inside the statement}} + [[clang::nounwind]] label: x = 1; // expected-warning {{'nounwind' attribute only applies to functions and statements}} + + [[clang::nounwind]] nounwind_fn(); + + __attribute__((nounwind)) bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::nounwind]]' on statements}} +} + +[[clang::nounwind]] static int i = bar(); // expected-warning {{'nounwind' attribute only applies to functions and statements}} diff --git a/clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp b/clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp --- a/clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp +++ b/clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp @@ -9,12 +9,12 @@ int i; ~B_ShouldDiag() noexcept(true) {} //no disg, no throw stmt }; -struct R_ShouldDiag : A_ShouldDiag { +struct R_ShouldDiag_NoThrow : A_ShouldDiag { B_ShouldDiag b; - ~R_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}} + ~R_ShouldDiag_NoThrow() { // expected-note {{destructor has a implicit non-throwing exception specification}} throw 1; // expected-warning {{has a non-throwing exception specification but}} } - __attribute__((nothrow)) R_ShouldDiag() {// expected-note {{function declared non-throwing here}} + __attribute__((nothrow)) R_ShouldDiag_NoThrow() {// expected-note {{function declared non-throwing here}} throw 1;// expected-warning {{has a non-throwing exception specification but}} } void __attribute__((nothrow)) SomeThrow() {// expected-note {{function declared non-throwing here}} @@ -24,6 +24,18 @@ throw 1; // expected-warning {{has a non-throwing exception specification but}} } }; +struct R_ShouldDiag_NoUnwind : A_ShouldDiag { + B_ShouldDiag b; + ~R_ShouldDiag_NoUnwind() { // expected-note {{destructor has a implicit non-throwing exception specification}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } + __attribute__((nounwind)) R_ShouldDiag_NoUnwind() {// expected-note {{function declared non-throwing here}} + throw 1;// expected-warning {{has a non-throwing exception specification but}} + } + void __attribute__((nounwind)) SomeThrow() {// expected-note {{function declared non-throwing here}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; struct M_ShouldNotDiag { B_ShouldDiag b; @@ -240,7 +252,7 @@ } } // As seen in p34973, this should not throw the warning. If there is an active -// exception, catch(...) catches everything. +// exception, catch(...) catches everything. void o_ShouldNotDiag() noexcept { try { throw;