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/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -306,6 +306,9 @@ `Issue 58229 `_ - The builtin type trait ``__is_aggregate`` now returns ``true`` for arrays of incomplete types in accordance with the suggested fix for `LWG3823 https://cplusplus.github.io/LWG/issue3823`_ +- ``__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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -524,6 +527,18 @@ used ``201904L`` (the date the proposal was seen by the committee) by mistake. There were no other changes to the attribute behavior. +- ``__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 @@ -823,8 +838,8 @@ - Introduced the new function ``clang_CXXMethod_isMoveAssignmentOperator``, which identifies whether a method cursor is a move-assignment operator. -- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``, - ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and +- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``, + ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and ``clang_Cursor_getTemplateArgumentUnsignedValue`` now work on struct, class, and partial template specialization cursors in addition to function cursors. 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; } @@ -4096,3 +4096,12 @@ let Subjects = SubjectList<[Function]>; let Documentation = [FunctionReturnThunksDocs]; } + +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,6 +535,61 @@ }]; } +def PureDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +A function declared with ``__attribute__((pure))`` attribute can 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 can 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 exception 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 ``__attribute__((pure))`` attribute. + +A function declared with ``__attribute__((const))`` attribute can 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 can not cause any observable side-effects other than +returning a value. They can not write to memory, but may 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 exception 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::noinline]]`` 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 = [{ @@ -4560,6 +4615,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. }]; } 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 @@ -112,7 +112,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 @@ -11478,7 +11478,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 @@ -2221,7 +2221,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 @@ -2137,7 +2137,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); @@ -2175,16 +2176,17 @@ 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()) { @@ -5316,6 +5318,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 @@ -696,6 +696,7 @@ } void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) { + bool nounwind = false; bool nomerge = false; bool noinline = false; bool alwaysinline = false; @@ -705,6 +706,9 @@ switch (A->getKind()) { default: break; + case attr::NoUnwind: + nounwind = true; + break; case attr::NoMerge: nomerge = true; break; @@ -721,6 +725,7 @@ break; } } + SaveAndRestore save_nounwind(InNoUnwindAttributedStmt, nounwind); SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge); SaveAndRestore save_noinline(InNoInlineAttributedStmt, noinline); SaveAndRestore save_alwaysinline(InAlwaysInlineAttributedStmt, @@ -2270,7 +2275,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, @@ -2280,6 +2286,8 @@ if (NoMerge) Result.addFnAttr(llvm::Attribute::NoMerge); + if (NoUnwind) + Result.addFnAttr(llvm::Attribute::NoUnwind); // Attach readnone and readonly attributes. if (!HasSideEffect) { if (ReadNone) @@ -2727,19 +2735,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 @@ -370,7 +370,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 @@ -15903,19 +15903,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: @@ -15923,8 +15927,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; } @@ -15934,8 +15941,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()) @@ -15990,6 +15998,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 @@ -192,6 +192,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,629 @@ +// 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 } undef, 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 } undef, 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 } undef, 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 "frame-pointer"="none" "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 "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #2 = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #3 = { noinline noreturn nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #4 = { mustprogress noinline optnone "frame-pointer"="none" "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,228 @@ +// 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 "frame-pointer"="none" "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 "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #2 = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #3 = { noinline noreturn nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK: attributes #4 = { mustprogress noinline optnone "frame-pointer"="none" "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/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;