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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to