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 @@ -51,6 +51,7 @@ } void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { + // FIXME: this does not consider 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 @@ -113,6 +113,8 @@ if (CallStack.count(Func)) return ExceptionInfo::createNonThrowing(); + // FIXME: this does not deal with NoThrow attribute. + if (const Stmt *Body = Func->getBody()) { CallStack.insert(Func); ExceptionInfo Result = 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,30 @@ } }; +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; +} + +// 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 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 +282,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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -592,6 +595,10 @@ 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. + 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,6 +4122,7 @@ let Subjects = SubjectList<[Function]>; let Documentation = [FunctionReturnThunksDocs]; } + def ReadOnlyPlacement : InheritableAttr { let Spellings = [Clang<"enforce_read_only_placement">]; let Subjects = SubjectList<[Record]>; 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,57 @@ }]; } +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 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 +1618,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 +3920,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 +4096,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 +4635,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 +6048,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/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2206,16 +2206,17 @@ FuncAttrs.addAttribute(llvm::Attribute::NoMerge); } - // 'const', 'pure' and 'noalias' attributed functions are also nounwind. if (TargetDecl->hasAttr()) { - FuncAttrs.addMemoryAttr(llvm::MemoryEffects::none()); - FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); + // We can not blindly state that the function does not acccess memory, + // since we do not know that it is `nounwind`. We do that later, though. + // FIXME: we may need a better way to model this kind of memory effects. // gcc specifies that 'const' functions have greater restrictions than - // 'pure' functions, so they also cannot have infinite loops. + // 'pure' functions, so they, either, cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { - FuncAttrs.addMemoryAttr(llvm::MemoryEffects::readOnly()); - FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); + // We can not blindly state that the function does not acccess memory, + // since we do not know that it is `nounwind`. We do that later, though. + // FIXME: we may need a better way to model this kind of memory effects. // gcc specifies that 'pure' functions cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { @@ -2273,6 +2274,12 @@ // Override some default IR attributes based on declaration-specific // information. if (TargetDecl) { + if (FuncAttrs.contains(llvm::Attribute::NoUnwind)) { + if (TargetDecl->hasAttr()) + FuncAttrs.addMemoryAttr(llvm::MemoryEffects::none()); + else if (TargetDecl->hasAttr()) + FuncAttrs.addMemoryAttr(llvm::MemoryEffects::readOnly()); + } if (TargetDecl->hasAttr()) FuncAttrs.removeAttribute(llvm::Attribute::SpeculativeLoadHardening); if (TargetDecl->hasAttr()) @@ -5476,6 +5483,15 @@ CI->setMetadata("srcloc", MDT); } + // Late manifestation for `pure`/`const` attributes, now that we know whether + // or not a call to a function with these attributes is nounwind or not. + if (TargetDecl && CI->doesNotThrow()) { + if (TargetDecl->hasAttr()) + CI->setDoesNotAccessMemory(); + else if (TargetDecl->hasAttr()) + CI->setOnlyReadsMemory(); + } + // 4. Finish the call. // If the call doesn't return, finish the basic block and clear the 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 @@ -16122,8 +16122,8 @@ case Builtin::BIfma: case Builtin::BIfmaf: case Builtin::BIfmal: - FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); - break; + FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation())); + break; default: break; } 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 @@ -43,9 +43,8 @@ _Noreturn void f9a(void); void f9b(void) { f9a(); } -// FIXME: We should be setting nounwind on calls. // CHECK: call i32 @f10_t() -// CHECK: [[NUW_RN:#[0-9]+]] +// CHECK: [[OS_WR2:#[0-9]+]] // CHECK: { int __attribute__((const)) f10_t(void); int f10(void) { return f10_t(); } @@ -57,9 +56,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_WR:#[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 @@ -111,9 +110,9 @@ // CHECK: attributes [[NUW]] = { nounwind optsize{{.*}} } // CHECK: attributes [[AI]] = { alwaysinline nounwind optsize{{.*}} } -// CHECK: attributes [[NUW_OS_RN]] = { nounwind optsize willreturn memory(none){{.*}} } +// CHECK: attributes [[OS_WR]] = { optsize willreturn {{.*}} } // CHECK: attributes [[SR]] = { nounwind optsize{{.*}} "stackrealign"{{.*}} } // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} } // CHECK: attributes [[NR]] = { noreturn optsize } -// CHECK: attributes [[NUW_RN]] = { nounwind optsize willreturn memory(none) } +// CHECK: attributes [[OS_WR2]] = { optsize willreturn } // CHECK: attributes [[RT_CALL]] = { optsize returns_twice } diff --git a/clang/test/CodeGen/pragma-weak.c b/clang/test/CodeGen/pragma-weak.c --- a/clang/test/CodeGen/pragma-weak.c +++ b/clang/test/CodeGen/pragma-weak.c @@ -138,7 +138,7 @@ #pragma weak xxx = __xxx __attribute((pure,noinline,const)) void __xxx(void) { } -// CHECK: void @__xxx() [[RN:#[0-9]+]] +// CHECK: void @__xxx() [[WR:#[0-9]+]] ///////////// PR28611: Try multiple aliases of same undeclared symbol or alias #pragma weak undecfunc_alias1 = undecfunc @@ -202,4 +202,4 @@ int correct_linkage; // CHECK: attributes [[NI]] = { noinline nounwind{{.*}} } -// CHECK: attributes [[RN]] = { noinline nounwind optnone willreturn memory(none){{.*}} } +// CHECK: attributes [[WR]] = { noinline nounwind optnone willreturn {{.*}} } 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 @@ -16,12 +16,11 @@ void *ps[] = { f0, f1, f2, f3, f4, f5 }; -// CHECK: declare i32 @f0() [[RN:#[0-9]+]] -// CHECK: declare i32 @f1() [[RO:#[0-9]+]] +// CHECK: declare i32 @f0() [[WR:#[0-9]+]] +// CHECK: declare i32 @f1() [[WR]] // CHECK: declare void @f2({{.*}} sret({{.*}}) align 4) // CHECK: declare void @f3({{.*}} sret({{.*}}) align 4) // 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 [[WR]] = { willreturn {{.*}} } 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,25 @@ // 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 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() [[WR_CALL:#[0-9]+]] + // CHECK: call noundef i32 @_Z1pv() [[WR_CALL]] + // CHECK: call noundef i32 @_Z3NoTv() [[NUW_CALL:#[0-9]+]] + return c() + p() + NoT() + t(); } -// CHECK: declare noundef i32 @_Z1cv() [[NUW_RN:#[0-9]+]] -// CHECK: declare noundef i32 @_Z1pv() [[NUW_RO:#[0-9]+]] +// CHECK: declare noundef i32 @_Z1cv() [[WR:#[0-9]+]] +// CHECK: declare noundef i32 @_Z1pv() [[WR]] +// CHECK: declare noundef i32 @_Z3NoTv() [[NUW:#[0-9]+]] // 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 [[WR]] = { willreturn {{.*}} } +// 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 [[WR_CALL]] = { willreturn } +// CHECK: attributes [[NUW_CALL]] = { nounwind } diff --git a/clang/test/CodeGenCXX/pr58798.cpp b/clang/test/CodeGenCXX/pr58798.cpp --- a/clang/test/CodeGenCXX/pr58798.cpp +++ b/clang/test/CodeGenCXX/pr58798.cpp @@ -1,18 +1,18 @@ // 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: Function Attrs: mustprogress noinline optnone willreturn{{$}} // 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: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR4:[0-9]+]] // CHECK-NEXT: ret i32 [[CALL]] // -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) +// CHECK: Function Attrs: mustprogress noinline optnone willreturn{{$}} // CHECK-LABEL: define {{[^@]+}}@_Z22callee_with_clang_attri // CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { // CHECK-NEXT: entry: @@ -22,9 +22,9 @@ // 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: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR5:[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: call void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR6:[0-9]+]] // CHECK-NEXT: unreachable // CHECK: if.end: // CHECK-NEXT: ret i32 24 @@ -32,23 +32,31 @@ // 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-SAME: (i32 noundef [[A:%.*]]) #[[ATTR1:[0-9]+]] 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: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3]] +// CHECK-NEXT: [[CALL:%.*]] = invoke noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR4]] +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK: invoke.cont: // CHECK-NEXT: ret i32 [[CALL]] +// 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]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: unreachable // -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) +// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn{{$}} // CHECK-LABEL: define {{[^@]+}}@_Z52early_caller_of_callee_with_cxx_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR3:[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 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR4]] +// CHECK-NEXT: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR5]] // CHECK-NEXT: ret i32 [[CALL]] // @@ -62,9 +70,9 @@ // 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: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR5]] // CHECK-NEXT: store i32 42, ptr [[EXCEPTION]], align 16 -// CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR5]] +// CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR6]] // CHECK-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD:%.*]] // CHECK: if.end: // CHECK-NEXT: ret i32 24 @@ -72,7 +80,7 @@ // 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: call void @__clang_call_terminate(ptr [[TMP2]]) #[[ATTR7]] // CHECK-NEXT: unreachable // CHECK: unreachable: // CHECK-NEXT: unreachable @@ -85,7 +93,7 @@ // 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: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR5]] // CHECK-NEXT: ret i32 [[CALL]] // @@ -129,14 +137,14 @@ // Calls to definitions: -// CHECK: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) +// CHECK: Function Attrs: mustprogress noinline optnone willreturn{{$}} // 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: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR4]] // CHECK-NEXT: ret i32 [[CALL]] // __attribute__((pure)) int late_caller_of_callee_with_clang_attr_with_clang_attr(int a) { @@ -145,26 +153,34 @@ // 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-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: [[CALL:%.*]] = call noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR3]] +// CHECK-NEXT: [[CALL:%.*]] = invoke noundef i32 @_Z22callee_with_clang_attri(i32 noundef [[TMP0]]) #[[ATTR4]] +// CHECK-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK: invoke.cont: // CHECK-NEXT: ret i32 [[CALL]] +// 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]]) #[[ATTR7]] +// CHECK-NEXT: unreachable // 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: Function Attrs: mustprogress noinline nounwind optnone willreturn{{$}} // CHECK-LABEL: define {{[^@]+}}@_Z51late_caller_of_callee_with_cxx_attr_with_clang_attri -// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR0]] { +// CHECK-SAME: (i32 noundef [[A:%.*]]) #[[ATTR3]] { // 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: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR5]] // CHECK-NEXT: ret i32 [[CALL]] // __attribute__((pure)) int late_caller_of_callee_with_cxx_attr_with_clang_attr(int a) { @@ -178,7 +194,7 @@ // 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: [[CALL:%.*]] = call noundef i32 @_Z20callee_with_cxx_attri(i32 noundef [[TMP0]]) #[[ATTR5]] // CHECK-NEXT: ret i32 [[CALL]] // int late_caller_of_callee_with_cxx_attr_with_cxx_attr(int a) noexcept { diff --git a/clang/test/CodeGenCXX/pure-const-attrs-and-ir-memory-attributes.cpp b/clang/test/CodeGenCXX/pure-const-attrs-and-ir-memory-attributes.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/pure-const-attrs-and-ir-memory-attributes.cpp @@ -0,0 +1,193 @@ +// 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 | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-NO-EXCEPTIONS +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-NO-EXCEPTIONS +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS + +void opaque_callee(); + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z13pure_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline optnone willreturn +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z13pure_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-EXCEPTIONS-NEXT: ret void +// +__attribute__((pure)) void pure_function() { + opaque_callee(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z14const_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR0]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline optnone willreturn +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z14const_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR0]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-EXCEPTIONS-NEXT: ret void +// +__attribute__((const)) void const_function() { + opaque_callee(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z30normal_caller_of_pure_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR2:[0-9]+]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z13pure_functionv() #[[ATTR5:[0-9]+]] +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline optnone +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z30normal_caller_of_pure_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR2:[0-9]+]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z13pure_functionv() #[[ATTR7:[0-9]+]] +// CHECK-EXCEPTIONS-NEXT: ret void +// +void normal_caller_of_pure_function() { + pure_function(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z31normal_caller_of_const_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR2]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14const_functionv() #[[ATTR6:[0-9]+]] +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline optnone +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z31normal_caller_of_const_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR2]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z14const_functionv() #[[ATTR7]] +// CHECK-EXCEPTIONS-NEXT: ret void +// +void normal_caller_of_const_function() { + const_function(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z22pure_noexcept_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR3:[0-9]+]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read) +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z22pure_noexcept_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: invoke void @_Z13opaque_calleev() +// CHECK-EXCEPTIONS-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-EXCEPTIONS: invoke.cont: +// CHECK-EXCEPTIONS-NEXT: ret void +// CHECK-EXCEPTIONS: terminate.lpad: +// CHECK-EXCEPTIONS-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-EXCEPTIONS-NEXT: catch ptr null +// CHECK-EXCEPTIONS-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-EXCEPTIONS-NEXT: call void @__clang_call_terminate(ptr [[TMP1]]) #[[ATTR8:[0-9]+]] +// CHECK-EXCEPTIONS-NEXT: unreachable +// +__attribute__((pure)) void pure_noexcept_function() noexcept { + opaque_callee(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(none) +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z23const_noexcept_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR4:[0-9]+]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z13opaque_calleev() +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(none) +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z23const_noexcept_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: invoke void @_Z13opaque_calleev() +// CHECK-EXCEPTIONS-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-EXCEPTIONS: invoke.cont: +// CHECK-EXCEPTIONS-NEXT: ret void +// CHECK-EXCEPTIONS: terminate.lpad: +// CHECK-EXCEPTIONS-NEXT: [[TMP0:%.*]] = landingpad { ptr, i32 } +// CHECK-EXCEPTIONS-NEXT: catch ptr null +// CHECK-EXCEPTIONS-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0 +// CHECK-EXCEPTIONS-NEXT: call void @__clang_call_terminate(ptr [[TMP1]]) #[[ATTR8]] +// CHECK-EXCEPTIONS-NEXT: unreachable +// +__attribute__((const)) void const_noexcept_function() noexcept { + opaque_callee(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z39normal_caller_of_pure_noexcept_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR2]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z22pure_noexcept_functionv() #[[ATTR7:[0-9]+]] +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z39normal_caller_of_pure_noexcept_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR6:[0-9]+]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z22pure_noexcept_functionv() #[[ATTR10:[0-9]+]] +// CHECK-EXCEPTIONS-NEXT: ret void +// +void normal_caller_of_pure_noexcept_function() { + pure_noexcept_function(); +} + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z40normal_caller_of_const_noexcept_functionv +// CHECK-NO-EXCEPTIONS-SAME: () #[[ATTR2]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z23const_noexcept_functionv() #[[ATTR8:[0-9]+]] +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z40normal_caller_of_const_noexcept_functionv +// CHECK-EXCEPTIONS-SAME: () #[[ATTR6]] { +// CHECK-EXCEPTIONS-NEXT: entry: +// CHECK-EXCEPTIONS-NEXT: call void @_Z23const_noexcept_functionv() #[[ATTR11:[0-9]+]] +// CHECK-EXCEPTIONS-NEXT: ret void +// +void normal_caller_of_const_noexcept_function() { + const_noexcept_function(); +} +//. +// CHECK-NO-EXCEPTIONS: attributes #0 = { mustprogress noinline nounwind optnone willreturn "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #2 = { 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-NO-EXCEPTIONS: attributes #3 = { mustprogress noinline nounwind optnone willreturn memory(read) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #4 = { mustprogress noinline nounwind optnone willreturn memory(none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #5 = { willreturn memory(read) } +// CHECK-NO-EXCEPTIONS: attributes #6 = { willreturn memory(none) } +// CHECK-NO-EXCEPTIONS: attributes #7 = { nounwind willreturn memory(read) } +// CHECK-NO-EXCEPTIONS: attributes #8 = { nounwind willreturn memory(none) } +//. +// CHECK-EXCEPTIONS: attributes #0 = { mustprogress noinline optnone willreturn "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-EXCEPTIONS: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-EXCEPTIONS: attributes #2 = { 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-EXCEPTIONS: attributes #3 = { mustprogress noinline nounwind optnone willreturn memory(read) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-EXCEPTIONS: attributes #4 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-EXCEPTIONS: attributes #5 = { mustprogress noinline nounwind optnone willreturn memory(none) "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-EXCEPTIONS: attributes #6 = { 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-EXCEPTIONS: attributes #7 = { willreturn } +// CHECK-EXCEPTIONS: attributes #8 = { noreturn nounwind } +// CHECK-EXCEPTIONS: attributes #9 = { nounwind } +// CHECK-EXCEPTIONS: attributes #10 = { nounwind willreturn memory(read) } +// CHECK-EXCEPTIONS: attributes #11 = { nounwind willreturn memory(none) } +//. +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// CHECK-ALL: {{.*}} diff --git a/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl b/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl --- a/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl +++ b/clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl @@ -17,16 +17,18 @@ } // Test that Attr.Const from OpenCLBuiltins.td is lowered to a readnone attribute. +// FIXME: we don't, though. // CHECK-LABEL: @test_const_attr -// CHECK: call spir_func i32 @_Z3maxii({{.*}}) [[ATTR_CONST:#[0-9]]] +// CHECK: call spir_func i32 @_Z3maxii({{.*}}) [[ATTR_CONST_OR_PURE:#[0-9]]] // CHECK: ret int test_const_attr(int a) { return max(a, 2); } // Test that Attr.Pure from OpenCLBuiltins.td is lowered to a readonly attribute. +// FIXME: we don't, though. // CHECK-LABEL: @test_pure_attr -// CHECK: call spir_func <4 x float> @_Z11read_imagef{{.*}} [[ATTR_PURE:#[0-9]]] +// CHECK: call spir_func <4 x float> @_Z11read_imagef{{.*}} [[ATTR_CONST_OR_PURE]] // CHECK: ret kernel void test_pure_attr(read_only image1d_t img) { float4 resf = read_imagef(img, 42); @@ -48,7 +50,5 @@ float res = fract(a, b); } -// CHECK: attributes [[ATTR_CONST]] = -// CHECK-SAME: memory(none) -// CHECK: attributes [[ATTR_PURE]] = -// CHECK-SAME: memory(read) +// CHECK: attributes [[ATTR_CONST_OR_PURE]] = +// CHECK-NOT: memory(none) 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}} @@ -240,7 +240,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;