lebedev.ri created this revision.
lebedev.ri added reviewers: aaron.ballman, erichkeane, rjmccall.
lebedev.ri added a project: LLVM.
Herald added subscribers: carlosgalvezp, jdoerfert.
Herald added a reviewer: NoQ.
Herald added a reviewer: njames93.
Herald added a project: All.
lebedev.ri requested review of this revision.
Herald added projects: clang, clang-tools-extra.
Herald added a subscriber: cfe-commits.
This is an implementation of the following RFC:
https://discourse.llvm.org/t/rfc-better-ux-for-clangs-unwind-affecting-attributes/66890
In C++, there are 3 possible behaviors when the
exception escapes out an function that can not unwind:
1. exception propagates into function's caller
2. defined behavior of immediate program termination
3. the wild UB case, behavior is undefined.
Let's look at obvious examples:
1. exception propagates into caller, is caught there, and all is good:
https://godbolt.org/z/MbTW9rofn
2. exception can not exit `noexcept` function, program is terminated:
https://godbolt.org/z/ffeaPz1dK
Now, the third case, the wild UB case, is the most interesting one.
There are 3 clang/gcc attributes that are relevant here, let's look at them:
1. `__attribute__((pure))`: https://godbolt.org/z/PY3KrETb7, there the fun
begins. In clang, we get UB, in gcc we get "exception propagates into
function's caller"
2. `__attribute__((const))`: https://godbolt.org/z/ozxoW16e9, same as
`__attribute__((pure))`
3. `__attribute__((nothrow))`: https://godbolt.org/z/YMf4sTcfa, the behavior is
consistently defined as immediate program termination. I do not understand why
it was defined as such, in the sense of how is that different from plain
`noexcept`, but at least we are consistent.
Now, there are 3 problems:
1. Our modelling of `__attribute__((const))`/`__attribute__((pure))` differs
from that of GCC, we add UB.
2. We can not ask for `__attribute__((const))`/`__attribute__((pure))`
behavior, without it acting as exception barrier.
3. We can not separately ask for the exception propagation to be UB. This would
be a handy optimization tool, especially given how brittle our IRGen for the
case 2. (program termination) is.
Therefore, this patch does two things:
1. Match GCC's implementation-defined behavior on
`__attribute__((pure))`/`__attribute__((const))`
- they should not cause UB on exception escape, nor should they cause
immediate program termination, exceptions should be free to escape into their
caller.
2. Introduce `__attribute__((nounwind))`, which would be lowered into LLVM IR's
`nounwind` attribute, and if an exception escapes out of an function marked
with such an attribute, wild UB happens.
Please note, while currently such an UB is indeed not sanitized,
i have a patch in progress to handle it:
https://reviews.llvm.org/D137381,
so we would not be introducing something that is impossible to deal with.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D138958
Files:
clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/Builtins.h
clang/lib/AST/ASTContext.cpp
clang/lib/Analysis/CFG.cpp
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/lib/Sema/SemaStmtAttr.cpp
clang/test/CodeGen/function-attributes.c
clang/test/CodeGen/struct-passing.c
clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp
clang/test/CodeGenCXX/exception-escape-RAII-codegen.cpp
clang/test/CodeGenCXX/exception-escape-codegen.cpp
clang/test/CodeGenCXX/pr58798.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp
Index: clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp
===================================================================
--- clang/test/SemaCXX/warn-throw-out-noexcept-func.cpp
+++ 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;
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ 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)
Index: clang/test/CodeGenCXX/pr58798.cpp
===================================================================
--- 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);
-}
Index: clang/test/CodeGenCXX/exception-escape-codegen.cpp
===================================================================
--- /dev/null
+++ 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 }
Index: clang/test/CodeGenCXX/exception-escape-RAII-codegen.cpp
===================================================================
--- /dev/null
+++ 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 }
Index: clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp
===================================================================
--- clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp
+++ 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 }
Index: clang/test/CodeGen/struct-passing.c
===================================================================
--- clang/test/CodeGen/struct-passing.c
+++ 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){{.*}} }
Index: clang/test/CodeGen/function-attributes.c
===================================================================
--- clang/test/CodeGen/function-attributes.c
+++ 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(); }
// <rdar://problem/7102668> [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 }
Index: clang/lib/Sema/SemaStmtAttr.cpp
===================================================================
--- clang/lib/Sema/SemaStmtAttr.cpp
+++ 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:
Index: clang/lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- clang/lib/Sema/SemaExceptionSpec.cpp
+++ 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<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
+ // As an extension, we assume that __attribute__((nounwind)) and
+ // __attribute__((nothrow)) functions don't throw.
+ if (D && isa<FunctionDecl>(D) &&
+ (D->hasAttr<NoUnwindAttr>() || D->hasAttr<NoThrowAttr>()))
return CT_Cannot;
QualType T;
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -192,6 +192,7 @@
if (ComputedEST == EST_None)
return;
+ // FIXME: what about NoUnwindAttr?
if (EST == EST_None && Method->hasAttr<NoThrowAttr>())
EST = EST_BasicNoexcept;
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -15903,19 +15903,23 @@
Context.BuiltinInfo.isConstWithoutErrnoAndExceptions(BuiltinID);
bool ConstWithoutExceptions =
Context.BuiltinInfo.isConstWithoutExceptions(BuiltinID);
- if (!FD->hasAttr<ConstAttr>() &&
+ if ((!FD->hasAttr<ConstAttr>() || !FD->hasAttr<NoUnwindAttr>()) &&
(ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) &&
(!ConstWithoutErrnoAndExceptions ||
(!getLangOpts().MathErrno && NoExceptions)) &&
- (!ConstWithoutExceptions || NoExceptions))
- FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation()));
+ (!ConstWithoutExceptions || NoExceptions)) {
+ if (!FD->hasAttr<ConstAttr>())
+ FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation()));
+ if (!FD->hasAttr<NoUnwindAttr>())
+ 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<ConstAttr>()) {
+ (!FD->hasAttr<ConstAttr>() || !FD->hasAttr<NoUnwindAttr>())) {
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<ConstAttr>())
+ FD->addAttr(ConstAttr::CreateImplicit(Context, FD->getLocation()));
+ if (!FD->hasAttr<NoUnwindAttr>())
+ FD->addAttr(NoUnwindAttr::CreateImplicit(Context, FD->getLocation()));
+ break;
default:
break;
}
@@ -15934,8 +15941,9 @@
!FD->hasAttr<ReturnsTwiceAttr>())
FD->addAttr(ReturnsTwiceAttr::CreateImplicit(Context,
FD->getLocation()));
- if (Context.BuiltinInfo.isNoThrow(BuiltinID) && !FD->hasAttr<NoThrowAttr>())
- FD->addAttr(NoThrowAttr::CreateImplicit(Context, FD->getLocation()));
+ if (Context.BuiltinInfo.isNoUnwind(BuiltinID) &&
+ !FD->hasAttr<NoUnwindAttr>())
+ FD->addAttr(NoUnwindAttr::CreateImplicit(Context, FD->getLocation()));
if (Context.BuiltinInfo.isPure(BuiltinID) && !FD->hasAttr<PureAttr>())
FD->addAttr(PureAttr::CreateImplicit(Context, FD->getLocation()));
if (Context.BuiltinInfo.isConst(BuiltinID) && !FD->hasAttr<ConstAttr>())
@@ -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<NoThrowAttr>()) {
const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -370,7 +370,8 @@
static bool isNoexcept(const FunctionDecl *FD) {
const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
- if (FPT->isNothrow() || FD->hasAttr<NoThrowAttr>())
+ if (FPT->isNothrow() || FD->hasAttr<NoUnwindAttr>() ||
+ FD->hasAttr<NoThrowAttr>())
return true;
return false;
}
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ 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;
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ 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<bool> save_nounwind(InNoUnwindAttributedStmt, nounwind);
SaveAndRestore<bool> save_nomerge(InNoMergeAttributedStmt, nomerge);
SaveAndRestore<bool> save_noinline(InNoInlineAttributedStmt, noinline);
SaveAndRestore<bool> 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<llvm::Type *> &ResultRegTypes,
const std::vector<llvm::Type *> &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<llvm::CallBase>(*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<llvm::CallBase>(*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());
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -2137,7 +2137,8 @@
if (TargetDecl) {
if (TargetDecl->hasAttr<ReturnsTwiceAttr>())
FuncAttrs.addAttribute(llvm::Attribute::ReturnsTwice);
- if (TargetDecl->hasAttr<NoThrowAttr>())
+ if (TargetDecl->hasAttr<NoUnwindAttr>() ||
+ TargetDecl->hasAttr<NoThrowAttr>())
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
if (TargetDecl->hasAttr<NoReturnAttr>())
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<NoUnwindAttr>()) {
+ FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
+ }
+
if (TargetDecl->hasAttr<ConstAttr>()) {
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<PureAttr>()) {
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<NoAliasAttr>()) {
@@ -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);
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -2221,7 +2221,7 @@
getContext().BuiltinInfo.isConstWithoutErrnoAndExceptions(BuiltinID);
bool ConstWithoutExceptions =
getContext().BuiltinInfo.isConstWithoutExceptions(BuiltinID);
- if (FD->hasAttr<ConstAttr>() ||
+ if ((FD->hasAttr<ConstAttr>() && FD->hasAttr<NoUnwindAttr>()) ||
((ConstWithoutErrnoAndExceptions || ConstWithoutExceptions) &&
(!ConstWithoutErrnoAndExceptions || (!getLangOpts().MathErrno)))) {
switch (BuiltinIDIfNoAsmLabel) {
Index: clang/lib/Analysis/CFG.cpp
===================================================================
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -2722,7 +2722,7 @@
if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context))
NoReturn = true;
- if (FD->hasAttr<NoThrowAttr>())
+ if (FD->hasAttr<NoUnwindAttr>() || FD->hasAttr<NoThrowAttr>())
AddEHEdge = false;
if (FD->getBuiltinID() == Builtin::BI__builtin_object_size ||
FD->getBuiltinID() == Builtin::BI__builtin_dynamic_object_size)
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ 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;
Index: clang/include/clang/Basic/Builtins.h
===================================================================
--- clang/include/clang/Basic/Builtins.h
+++ 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;
}
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ 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.
}];
}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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;
+}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -306,6 +306,9 @@
`Issue 58229 <https://github.com/llvm/llvm-project/issues/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.
Index: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
===================================================================
--- clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
+++ 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<CallExpr>(St)) {
+ // FIXME: this does not deal with stmt NoUnwind attribute.
if (const FunctionDecl *Func = Call->getDirectCallee()) {
ExceptionInfo Excs = throwsException(Func, CallStack);
Results.merge(Excs);
Index: clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/ExceptionEscapeCheck.cpp
+++ 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()),
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits