lebedev.ri updated this revision to Diff 475894.
lebedev.ri added a comment.
Update tests.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D137381/new/
https://reviews.llvm.org/D137381
Files:
clang/docs/ReleaseNotes.rst
clang/docs/UndefinedBehaviorSanitizer.rst
clang/include/clang/Basic/Sanitizers.def
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CGCleanup.cpp
clang/lib/CodeGen/CGCleanup.h
clang/lib/CodeGen/CGException.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/CodeGen/EHScopeStack.h
clang/lib/Driver/SanitizerArgs.cpp
clang/test/CodeGenCXX/catch-exception-escape.cpp
clang/test/Driver/fsanitize.c
compiler-rt/lib/ubsan/ubsan_checks.inc
compiler-rt/lib/ubsan/ubsan_handlers.cpp
compiler-rt/lib/ubsan/ubsan_handlers.h
compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
compiler-rt/test/ubsan/TestCases/Misc/exception-escape.cpp
compiler-rt/test/ubsan_minimal/TestCases/exception-escape.cpp
Index: compiler-rt/test/ubsan_minimal/TestCases/exception-escape.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan_minimal/TestCases/exception-escape.cpp
@@ -0,0 +1,48 @@
+// RUN: %clangxx -fsanitize=exception-escape %s -O3 -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not="exception-escape" --check-prefixes=CHECK-ALL,CHECK-ONE
+// RUN: not --crash %run %t a 2>&1 | FileCheck %s --implicit-check-not="exception-escape" --check-prefixes=CHECK-ALL,CHECK-TWO
+// RUN: not --crash %run %t a b 2>&1 | FileCheck %s --implicit-check-not="exception-escape" --check-prefixes=CHECK-ALL,CHECK-THREE
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void thrower() { throw 0; }
+
+void nonthrower() noexcept {}
+
+// pure functions are generally side-effect free,
+// so we need to be smart to defeat optimizer
+// from completely dropping the call to the function...
+
+void *__attribute__((pure)) footgun(int x) {
+ if (x == 2)
+ thrower();
+ if (x == 3)
+ thrower();
+ nonthrower();
+ fprintf(stderr, "SUCCESS\n");
+ return malloc(1);
+}
+
+// CHECK-ALL: TEST
+
+// CHECK-ONE: SUCCESS
+// CHECK-TWO: exception-escape
+// CHECK-THREE: exception-escape
+
+int main(int argc, char **argv) {
+ bool status = 0;
+ void *mem = nullptr;
+
+ fprintf(stderr, "TEST\n");
+
+ try {
+ mem = footgun(argc);
+ status = !(mem != 0);
+ } catch (...) {
+ }
+
+ fprintf(stderr, "escaping: %p\n", mem);
+ free(mem);
+ return status;
+}
Index: compiler-rt/test/ubsan/TestCases/Misc/exception-escape.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/ubsan/TestCases/Misc/exception-escape.cpp
@@ -0,0 +1,104 @@
+// RUN: %clangxx -fsanitize=exception-escape %s -O3 -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-ONE
+// RUN: not %run %t 2 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-TWO
+// RUN: not %run %t 2 3 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-THREE
+// RUN: %run %t 2 3 4 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-FOUR
+// RUN: not %run %t 2 3 4 5 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-FIVE
+// RUN: not %run %t 2 3 4 5 6 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-SIX
+// RUN: not %run %t 2 3 4 5 6 6 2>&1 | FileCheck %s --implicit-check-not="error:" --check-prefixes=CHECK-ALL,CHECK-SEVEN
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void thrower() { throw 0; }
+
+void maybe_throws() {}
+
+void nonthrower() noexcept {}
+
+// pure functions are generally side-effect free,
+// so we need to be smart to defeat optimizer
+// from completely dropping the call to the function...
+
+void *__attribute__((pure)) footgun(int x) {
+ if (x == 2)
+#line 100
+ thrower();
+
+ if (x == 3)
+#line 200
+ thrower();
+
+ nonthrower();
+
+ if (x == 4) {
+ try {
+#line 300
+ thrower();
+ } catch (...) {
+ }
+ }
+
+ if (x == 5) {
+ try {
+#line 400
+ thrower();
+ } catch (...) {
+#line 500
+ throw;
+ }
+ }
+
+ if (x == 6) {
+ try {
+#line 600
+ thrower();
+ } catch (...) {
+#line 700
+ maybe_throws();
+#line 800
+ throw;
+ }
+ }
+
+ if (x == 7) {
+ try {
+#line 900
+ thrower();
+ } catch (...) {
+#line 1000
+ thrower();
+#line 1100
+ throw;
+ }
+ }
+
+ fprintf(stderr, "SUCCESS\n"); // Should be here, not in `main()`.
+ return malloc(1);
+}
+
+// CHECK-ALL: TEST
+
+// CHECK-ONE: SUCCESS
+// CHECK-TWO: exception-escape.cpp:100:5: runtime error: exception escapes out of function that should not throw exception
+// CHECK-THREE: exception-escape.cpp:200:5: runtime error: exception escapes out of function that should not throw exception
+// CHECK-FOUR: SUCCESS
+// CHECK-FIVE: exception-escape.cpp:400:7: runtime error: exception escapes out of function that should not throw exception
+// CHECK-SIX: exception-escape.cpp:600:7: runtime error: exception escapes out of function that should not throw exception
+// CHECK-SEVEN: exception-escape.cpp:1000:7: runtime error: exception escapes out of function that should not throw exception
+
+int main(int argc, char **argv) {
+ bool status = 0;
+ void *mem = nullptr;
+
+ fprintf(stderr, "TEST\n");
+
+ try {
+ mem = footgun(argc);
+ status = !(mem != 0);
+ } catch (...) {
+ }
+
+ fprintf(stderr, "escaping: %p\n", mem);
+ return status;
+}
Index: compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
===================================================================
--- compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
+++ compiler-rt/lib/ubsan_minimal/ubsan_minimal_handlers.cpp
@@ -141,3 +141,4 @@
HANDLER(nullability_return, "nullability-return")
HANDLER(pointer_overflow, "pointer-overflow")
HANDLER(cfi_check_fail, "cfi-check-fail")
+HANDLER(exception_escape, "exception-escape")
Index: compiler-rt/lib/ubsan/ubsan_handlers.h
===================================================================
--- compiler-rt/lib/ubsan/ubsan_handlers.h
+++ compiler-rt/lib/ubsan/ubsan_handlers.h
@@ -99,6 +99,13 @@
/// \brief Handle reaching the end of a value-returning function.
UNRECOVERABLE(missing_return, UnreachableData *Data)
+struct ExceptionEscapeData {
+ SourceLocation Loc;
+};
+
+/// \brief Handle exception escaping out of C++ `noexcept` function.
+UNRECOVERABLE(exception_escape, ExceptionEscapeData *Data, ValueHandle Exn)
+
struct VLABoundData {
SourceLocation Loc;
const TypeDescriptor &Type;
Index: compiler-rt/lib/ubsan/ubsan_handlers.cpp
===================================================================
--- compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -433,6 +433,17 @@
Die();
}
+void __ubsan::__ubsan_handle_exception_escape(ExceptionEscapeData *Data,
+ ValueHandle Exn) {
+ GET_REPORT_OPTIONS(true);
+ ErrorType ET = ErrorType::ExceptionEscape;
+ ScopedReport R(Opts, Data->Loc, ET);
+ Diag(Data->Loc, DL_Error, ET,
+ "exception escapes out of function that should not throw exception");
+ // FIXME: can we do anything useful with the \p Exn?
+ Die();
+}
+
static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
Index: compiler-rt/lib/ubsan/ubsan_checks.inc
===================================================================
--- compiler-rt/lib/ubsan/ubsan_checks.inc
+++ compiler-rt/lib/ubsan/ubsan_checks.inc
@@ -69,3 +69,4 @@
"nullability-arg")
UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "vptr")
UBSAN_CHECK(CFIBadType, "cfi-bad-type", "cfi")
+UBSAN_CHECK(ExceptionEscape, "exception-escape", "exception-escape")
Index: clang/test/Driver/fsanitize.c
===================================================================
--- clang/test/Driver/fsanitize.c
+++ clang/test/Driver/fsanitize.c
@@ -5,15 +5,15 @@
// RUN: %clang --target=x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
// RUN: %clang --target=x86_64-linux-gnu -fsanitize-trap -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
// CHECK-UNDEFINED-TRAP-NOT: -fsanitize-recover
-// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
-// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
-// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function|exception-escape),?){19}"}}
+// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound,exception-escape"
+// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound,exception-escape"
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
-// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
+// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|exception-escape),?){20}"}}
// RUN: %clang --target=x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
-// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
+// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|exception-escape),?){19}"}}
// RUN: %clang --target=i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-UNDEFINED-WIN32,CHECK-UNDEFINED-MSVC
// RUN: %clang --target=i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-UNDEFINED-WIN32,CHECK-UNDEFINED-WIN-CXX,CHECK-UNDEFINED-MSVC
@@ -24,8 +24,8 @@
// CHECK-UNDEFINED-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone{{(-x86_64)?}}.lib"
// CHECK-UNDEFINED-WIN64-MINGW: "--dependent-lib={{[^"]*}}libclang_rt.ubsan_standalone{{(-x86_64)?}}.a"
// CHECK-UNDEFINED-WIN-CXX: "--dependent-lib={{[^"]*}}ubsan_standalone_cxx{{[^"]*}}.lib"
-// CHECK-UNDEFINED-MSVC-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
-// CHECK-UNDEFINED-WIN64-MINGW-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|vptr),?){18}"}}
+// CHECK-UNDEFINED-MSVC-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|exception-escape),?){18}"}}
+// CHECK-UNDEFINED-WIN64-MINGW-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|vptr|exception-escape),?){19}"}}
// RUN: %clang --target=i386-pc-win32 -fsanitize-coverage=bb %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COVERAGE-WIN32
// CHECK-COVERAGE-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone{{(-i386)?}}.lib"
@@ -90,7 +90,7 @@
// CHECK-FNO-SANITIZE-ALL: "-fsanitize=thread"
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,builtin,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
-// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){14}"}}
+// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute|exception-escape),?){15}"}}
// RUN: %clang -fsanitize=shift -fno-sanitize=shift-base %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSANITIZE-SHIFT-PARTIAL
// CHECK-FSANITIZE-SHIFT-PARTIAL: "-fsanitize=shift-exponent"
@@ -823,7 +823,7 @@
// CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=thread'
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL
-// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|exception-escape),?){18}"}}
// CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=integer -fsanitize-trap=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTSAN-TRAP
Index: clang/test/CodeGenCXX/catch-exception-escape.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/catch-exception-escape.cpp
@@ -0,0 +1,903 @@
+// 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 -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-NO-EXCEPTIONS
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-NOSANITIZE
+// RUN: %clang_cc1 -fsanitize=exception-escape -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-SANITIZE-NORECOVER
+// RUN: %clang_cc1 -fsanitize=exception-escape -fno-sanitize-recover=exception-escape -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-SANITIZE-NORECOVER
+// RUN: %clang_cc1 -fsanitize=exception-escape -fsanitize-trap=exception-escape -emit-llvm %s -o - -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions | FileCheck %s --check-prefixes=CHECK-SANITIZE-TRAP
+
+void will_throw();
+void might_throw();
+void will_not_throw() noexcept;
+
+// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read)
+// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z30footgun_exception_escape_is_ubi
+// CHECK-NO-EXCEPTIONS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NO-EXCEPTIONS-NEXT: entry:
+// CHECK-NO-EXCEPTIONS-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NO-EXCEPTIONS-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]]
+// CHECK-NO-EXCEPTIONS: if.end:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4:[0-9]+]]
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then2:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END3]]
+// CHECK-NO-EXCEPTIONS: if.end3:
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END6:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then5:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4]]
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END6]]
+// CHECK-NO-EXCEPTIONS: if.end6:
+// CHECK-NO-EXCEPTIONS-NEXT: ret void
+//
+// CHECK-NOSANITIZE: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read)
+// CHECK-NOSANITIZE-LABEL: define {{[^@]+}}@_Z30footgun_exception_escape_is_ubi
+// CHECK-NOSANITIZE-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-NOSANITIZE-NEXT: entry:
+// CHECK-NOSANITIZE-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-NOSANITIZE-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NOSANITIZE: if.then:
+// CHECK-NOSANITIZE-NEXT: call void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END]]
+// CHECK-NOSANITIZE: if.end:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6:[0-9]+]]
+// CHECK-NOSANITIZE-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-NOSANITIZE: if.then2:
+// CHECK-NOSANITIZE-NEXT: call void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END3]]
+// CHECK-NOSANITIZE: if.end3:
+// CHECK-NOSANITIZE-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]]
+// CHECK-NOSANITIZE: if.then5:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-NOSANITIZE: lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-NOSANITIZE-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-NOSANITIZE-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: br label [[CATCH:%.*]]
+// CHECK-NOSANITIZE: catch:
+// CHECK-NOSANITIZE-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z11might_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]]
+// CHECK-NOSANITIZE: invoke.cont7:
+// CHECK-NOSANITIZE-NEXT: invoke void @__cxa_rethrow() #[[ATTR7:[0-9]+]]
+// CHECK-NOSANITIZE-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]]
+// CHECK-NOSANITIZE: lpad6:
+// CHECK-NOSANITIZE-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: cleanup
+// CHECK-NOSANITIZE-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-NOSANITIZE-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1
+// CHECK-NOSANITIZE-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont8:
+// CHECK-NOSANITIZE-NEXT: br label [[EH_RESUME:%.*]]
+// CHECK-NOSANITIZE: try.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END9]]
+// CHECK-NOSANITIZE: if.end9:
+// CHECK-NOSANITIZE-NEXT: ret void
+// CHECK-NOSANITIZE: eh.resume:
+// CHECK-NOSANITIZE-NEXT: [[EXN10:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } undef, ptr [[EXN10]], 0
+// CHECK-NOSANITIZE-NEXT: [[LPAD_VAL11:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1
+// CHECK-NOSANITIZE-NEXT: resume { ptr, i32 } [[LPAD_VAL11]]
+// CHECK-NOSANITIZE: terminate.lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0
+// CHECK-NOSANITIZE-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR8:[0-9]+]]
+// CHECK-NOSANITIZE-NEXT: unreachable
+// CHECK-NOSANITIZE: unreachable:
+// CHECK-NOSANITIZE-NEXT: unreachable
+//
+// CHECK-SANITIZE-NORECOVER: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read)
+// CHECK-SANITIZE-NORECOVER-LABEL: define {{[^@]+}}@_Z30footgun_exception_escape_is_ubi
+// CHECK-SANITIZE-NORECOVER-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-NORECOVER-NEXT: entry:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[INVOKE_SRCLOC:%.*]] = alloca { ptr, i32, i32 }, align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP:%.*]] = alloca { ptr, i32, i32 }, align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then:
+// CHECK-SANITIZE-NORECOVER-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 100, i32 5 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[EXCEPTION_ESCAPE_LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-NORECOVER: handler.exception_escape:
+// CHECK-SANITIZE-NORECOVER-NEXT: store { ptr, i32, i32 } [[CURR_INVOKE_SRCLOC:%.*]], ptr [[TMP]], align 8, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP]] to i64, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[EXN12:%.*]] to i64, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_exception_escape(i64 [[TMP1]], i64 [[TMP2]]) #[[ATTR7:[0-9]+]], !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER: cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER: if.end:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8:[0-9]+]]
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP3:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP3]], 3
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then2:
+// CHECK-SANITIZE-NORECOVER-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 300, i32 5 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[EXCEPTION_ESCAPE_LPAD]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont3:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END4]]
+// CHECK-SANITIZE-NORECOVER: if.end4:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP4:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP4]], 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END11:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then6:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 500, i32 7 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont7:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-NORECOVER: lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP5:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP6]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[TMP7]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-NORECOVER: catch:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP8:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 700, i32 7 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT9:%.*]] unwind label [[LPAD8:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont9:
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @__cxa_rethrow() #[[ATTR9:[0-9]+]]
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD8]]
+// CHECK-SANITIZE-NORECOVER: lpad8:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP9:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP10:%.*]] = extractvalue { ptr, i32 } [[TMP9]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP10]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP9]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[TMP11]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @__cxa_end_catch()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT10:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont10:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[EXCEPTION_ESCAPE_HANDLER:%.*]]
+// CHECK-SANITIZE-NORECOVER: try.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END11]]
+// CHECK-SANITIZE-NORECOVER: if.end11:
+// CHECK-SANITIZE-NORECOVER-NEXT: ret void
+// CHECK-SANITIZE-NORECOVER: terminate.lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP12:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @__clang_call_terminate(ptr [[TMP13]]) #[[ATTR7]]
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+// CHECK-SANITIZE-NORECOVER: unreachable:
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+// CHECK-SANITIZE-NORECOVER: exception-escape.sanitization:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN12]] = load ptr, ptr [[EXN_SLOT]], align 8, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CURR_INVOKE_SRCLOC]] = load { ptr, i32, i32 }, ptr [[INVOKE_SRCLOC]], align 8, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 false, label [[CONT:%.*]], label [[HANDLER_EXCEPTION_ESCAPE:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize !2
+// CHECK-SANITIZE-NORECOVER: exception-escape.lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP14:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP15:%.*]] = extractvalue { ptr, i32 } [[TMP14]], 0, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP15]], ptr [[EXN_SLOT]], align 8, !nosanitize !2
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[EXCEPTION_ESCAPE_SANITIZATION:%.*]], !nosanitize !2
+// CHECK-SANITIZE-NORECOVER: exception-escape.handler:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[EXCEPTION_ESCAPE_SANITIZATION]]
+//
+// CHECK-SANITIZE-TRAP: Function Attrs: mustprogress noinline nounwind optnone willreturn memory(read)
+// CHECK-SANITIZE-TRAP-LABEL: define {{[^@]+}}@_Z30footgun_exception_escape_is_ubi
+// CHECK-SANITIZE-TRAP-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-TRAP-NEXT: entry:
+// CHECK-SANITIZE-TRAP-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[INVOKE_SRCLOC:%.*]] = alloca { ptr, i32, i32 }, align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-TRAP: if.then:
+// CHECK-SANITIZE-TRAP-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 100, i32 5 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[EXCEPTION_ESCAPE_LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-TRAP: trap:
+// CHECK-SANITIZE-TRAP-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: unreachable, !nosanitize !2
+// CHECK-SANITIZE-TRAP: cont:
+// CHECK-SANITIZE-TRAP-NEXT: unreachable, !nosanitize !2
+// CHECK-SANITIZE-TRAP: if.end:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8:[0-9]+]]
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+// CHECK-SANITIZE-TRAP: if.then2:
+// CHECK-SANITIZE-TRAP-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 300, i32 5 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[EXCEPTION_ESCAPE_LPAD]]
+// CHECK-SANITIZE-TRAP: invoke.cont3:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END4]]
+// CHECK-SANITIZE-TRAP: if.end4:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END11:%.*]]
+// CHECK-SANITIZE-TRAP: if.then6:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 500, i32 7 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont7:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-TRAP: lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-TRAP: catch:
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: store { ptr, i32, i32 } { ptr @.src, i32 700, i32 7 }, ptr [[INVOKE_SRCLOC]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT9:%.*]] unwind label [[LPAD8:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont9:
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @__cxa_rethrow() #[[ATTR9:[0-9]+]]
+// CHECK-SANITIZE-TRAP-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD8]]
+// CHECK-SANITIZE-TRAP: lpad8:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @__cxa_end_catch()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT10:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont10:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[EXCEPTION_ESCAPE_HANDLER:%.*]]
+// CHECK-SANITIZE-TRAP: try.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END11]]
+// CHECK-SANITIZE-TRAP: if.end11:
+// CHECK-SANITIZE-TRAP-NEXT: ret void
+// CHECK-SANITIZE-TRAP: terminate.lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0
+// CHECK-SANITIZE-TRAP-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR7]]
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+// CHECK-SANITIZE-TRAP: unreachable:
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+// CHECK-SANITIZE-TRAP: exception-escape.sanitization:
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN12:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8, !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: [[CURR_INVOKE_SRCLOC:%.*]] = load { ptr, i32, i32 }, ptr [[INVOKE_SRCLOC]], align 8, !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize !2
+// CHECK-SANITIZE-TRAP: exception-escape.lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP12:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null, !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 0, !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP13]], ptr [[EXN_SLOT]], align 8, !nosanitize !2
+// CHECK-SANITIZE-TRAP-NEXT: br label [[EXCEPTION_ESCAPE_SANITIZATION:%.*]], !nosanitize !2
+// CHECK-SANITIZE-TRAP: exception-escape.handler:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[EXCEPTION_ESCAPE_SANITIZATION]]
+//
+void __attribute__((pure)) footgun_exception_escape_is_ub(int x) {
+ if (x == 2) {
+#line 100
+ will_throw();
+ }
+#line 200
+ will_not_throw();
+ if (x == 3) {
+#line 300
+ will_throw();
+ }
+ if (x == 4) {
+ try {
+#line 400
+ will_not_throw();
+#line 500
+ will_throw();
+ } catch (...) {
+#line 600
+ will_not_throw();
+#line 700
+ might_throw();
+#line 800
+ throw;
+ }
+ }
+}
+
+// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone
+// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z39exception_escape_is_program_terminationi
+// CHECK-NO-EXCEPTIONS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+// CHECK-NO-EXCEPTIONS-NEXT: entry:
+// CHECK-NO-EXCEPTIONS-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NO-EXCEPTIONS-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]]
+// CHECK-NO-EXCEPTIONS: if.end:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4]]
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then2:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END3]]
+// CHECK-NO-EXCEPTIONS: if.end3:
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END6:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then5:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4]]
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END6]]
+// CHECK-NO-EXCEPTIONS: if.end6:
+// CHECK-NO-EXCEPTIONS-NEXT: ret void
+//
+// CHECK-NOSANITIZE: Function Attrs: mustprogress noinline nounwind optnone
+// CHECK-NOSANITIZE-LABEL: define {{[^@]+}}@_Z39exception_escape_is_program_terminationi
+// CHECK-NOSANITIZE-SAME: (i32 noundef [[X:%.*]]) #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-NOSANITIZE-NEXT: entry:
+// CHECK-NOSANITIZE-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-NOSANITIZE-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NOSANITIZE: if.then:
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END]]
+// CHECK-NOSANITIZE: if.end:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+// CHECK-NOSANITIZE: if.then2:
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-NOSANITIZE: invoke.cont3:
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END4]]
+// CHECK-NOSANITIZE: if.end4:
+// CHECK-NOSANITIZE-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END9:%.*]]
+// CHECK-NOSANITIZE: if.then6:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont7:
+// CHECK-NOSANITIZE-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-NOSANITIZE: lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-NOSANITIZE-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-NOSANITIZE-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: br label [[CATCH:%.*]]
+// CHECK-NOSANITIZE: catch:
+// CHECK-NOSANITIZE-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z11might_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-NOSANITIZE: invoke.cont8:
+// CHECK-NOSANITIZE-NEXT: invoke void @__cxa_rethrow() #[[ATTR7]]
+// CHECK-NOSANITIZE-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-NOSANITIZE: try.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END9]]
+// CHECK-NOSANITIZE: if.end9:
+// CHECK-NOSANITIZE-NEXT: ret void
+// CHECK-NOSANITIZE: terminate.lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-NOSANITIZE-NEXT: call void @__clang_call_terminate(ptr [[TMP8]]) #[[ATTR8]]
+// CHECK-NOSANITIZE-NEXT: unreachable
+// CHECK-NOSANITIZE: unreachable:
+// CHECK-NOSANITIZE-NEXT: unreachable
+//
+// CHECK-SANITIZE-NORECOVER: Function Attrs: mustprogress noinline nounwind optnone
+// CHECK-SANITIZE-NORECOVER-LABEL: define {{[^@]+}}@_Z39exception_escape_is_program_terminationi
+// CHECK-SANITIZE-NORECOVER-SAME: (i32 noundef [[X:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-NORECOVER-NEXT: entry:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then:
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-NORECOVER: if.end:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then2:
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont3:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END4]]
+// CHECK-SANITIZE-NORECOVER: if.end4:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END9:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then6:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont7:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-NORECOVER: lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-NORECOVER: catch:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont8:
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @__cxa_rethrow() #[[ATTR9]]
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-NORECOVER: try.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END9]]
+// CHECK-SANITIZE-NORECOVER: if.end9:
+// CHECK-SANITIZE-NORECOVER-NEXT: ret void
+// CHECK-SANITIZE-NORECOVER: terminate.lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @__clang_call_terminate(ptr [[TMP8]]) #[[ATTR7]]
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+// CHECK-SANITIZE-NORECOVER: unreachable:
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+//
+// CHECK-SANITIZE-TRAP: Function Attrs: mustprogress noinline nounwind optnone
+// CHECK-SANITIZE-TRAP-LABEL: define {{[^@]+}}@_Z39exception_escape_is_program_terminationi
+// CHECK-SANITIZE-TRAP-SAME: (i32 noundef [[X:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-TRAP-NEXT: entry:
+// CHECK-SANITIZE-TRAP-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-TRAP: if.then:
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-TRAP: if.end:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+// CHECK-SANITIZE-TRAP: if.then2:
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-TRAP: invoke.cont3:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END4]]
+// CHECK-SANITIZE-TRAP: if.end4:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END9:%.*]]
+// CHECK-SANITIZE-TRAP: if.then6:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont7:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-TRAP: lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-TRAP: catch:
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-TRAP: invoke.cont8:
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @__cxa_rethrow() #[[ATTR9]]
+// CHECK-SANITIZE-TRAP-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD]]
+// CHECK-SANITIZE-TRAP: try.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END9]]
+// CHECK-SANITIZE-TRAP: if.end9:
+// CHECK-SANITIZE-TRAP-NEXT: ret void
+// CHECK-SANITIZE-TRAP: terminate.lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-SANITIZE-TRAP-NEXT: call void @__clang_call_terminate(ptr [[TMP8]]) #[[ATTR7]]
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+// CHECK-SANITIZE-TRAP: unreachable:
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+//
+void exception_escape_is_program_termination(int x) noexcept {
+ if (x == 2) {
+#line 100
+ will_throw();
+ }
+#line 200
+ will_not_throw();
+ if (x == 3) {
+#line 300
+ will_throw();
+ }
+ if (x == 4) {
+ try {
+#line 400
+ will_not_throw();
+#line 500
+ will_throw();
+ } catch (...) {
+#line 600
+ will_not_throw();
+#line 700
+ might_throw();
+#line 800
+ throw;
+ }
+ }
+}
+
+// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone
+// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki
+// CHECK-NO-EXCEPTIONS-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3]] {
+// CHECK-NO-EXCEPTIONS-NEXT: entry:
+// CHECK-NO-EXCEPTIONS-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NO-EXCEPTIONS-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]]
+// CHECK-NO-EXCEPTIONS: if.end:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4]]
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then2:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END3]]
+// CHECK-NO-EXCEPTIONS: if.end3:
+// CHECK-NO-EXCEPTIONS-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NO-EXCEPTIONS-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END6:%.*]]
+// CHECK-NO-EXCEPTIONS: if.then5:
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwv() #[[ATTR4]]
+// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwv()
+// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END6]]
+// CHECK-NO-EXCEPTIONS: if.end6:
+// CHECK-NO-EXCEPTIONS-NEXT: ret void
+//
+// CHECK-NOSANITIZE: Function Attrs: mustprogress noinline optnone
+// CHECK-NOSANITIZE-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki
+// CHECK-NOSANITIZE-SAME: (i32 noundef [[X:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-NOSANITIZE-NEXT: entry:
+// CHECK-NOSANITIZE-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-NOSANITIZE-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NOSANITIZE-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-NOSANITIZE: if.then:
+// CHECK-NOSANITIZE-NEXT: call void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END]]
+// CHECK-NOSANITIZE: if.end:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-NOSANITIZE: if.then2:
+// CHECK-NOSANITIZE-NEXT: call void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END3]]
+// CHECK-NOSANITIZE: if.end3:
+// CHECK-NOSANITIZE-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NOSANITIZE-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-NOSANITIZE-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]]
+// CHECK-NOSANITIZE: if.then5:
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z10will_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-NOSANITIZE: lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-NOSANITIZE-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-NOSANITIZE-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: br label [[CATCH:%.*]]
+// CHECK-NOSANITIZE: catch:
+// CHECK-NOSANITIZE-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: call void @_Z14will_not_throwv() #[[ATTR6]]
+// CHECK-NOSANITIZE-NEXT: invoke void @_Z11might_throwv()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]]
+// CHECK-NOSANITIZE: invoke.cont7:
+// CHECK-NOSANITIZE-NEXT: invoke void @__cxa_rethrow() #[[ATTR7]]
+// CHECK-NOSANITIZE-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]]
+// CHECK-NOSANITIZE: lpad6:
+// CHECK-NOSANITIZE-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: cleanup
+// CHECK-NOSANITIZE-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-NOSANITIZE-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1
+// CHECK-NOSANITIZE-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NOSANITIZE-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-NOSANITIZE: invoke.cont8:
+// CHECK-NOSANITIZE-NEXT: br label [[EH_RESUME:%.*]]
+// CHECK-NOSANITIZE: try.cont:
+// CHECK-NOSANITIZE-NEXT: br label [[IF_END9]]
+// CHECK-NOSANITIZE: if.end9:
+// CHECK-NOSANITIZE-NEXT: ret void
+// CHECK-NOSANITIZE: eh.resume:
+// CHECK-NOSANITIZE-NEXT: [[EXN10:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NOSANITIZE-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NOSANITIZE-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } undef, ptr [[EXN10]], 0
+// CHECK-NOSANITIZE-NEXT: [[LPAD_VAL11:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1
+// CHECK-NOSANITIZE-NEXT: resume { ptr, i32 } [[LPAD_VAL11]]
+// CHECK-NOSANITIZE: terminate.lpad:
+// CHECK-NOSANITIZE-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 }
+// CHECK-NOSANITIZE-NEXT: catch ptr null
+// CHECK-NOSANITIZE-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0
+// CHECK-NOSANITIZE-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR8]]
+// CHECK-NOSANITIZE-NEXT: unreachable
+// CHECK-NOSANITIZE: unreachable:
+// CHECK-NOSANITIZE-NEXT: unreachable
+//
+// CHECK-SANITIZE-NORECOVER: Function Attrs: mustprogress noinline optnone
+// CHECK-SANITIZE-NORECOVER-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki
+// CHECK-SANITIZE-NORECOVER-SAME: (i32 noundef [[X:%.*]]) #[[ATTR6:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-NORECOVER-NEXT: entry:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-NORECOVER: if.end:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then2:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END3]]
+// CHECK-SANITIZE-NORECOVER: if.end3:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]]
+// CHECK-SANITIZE-NORECOVER: if.then5:
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-NORECOVER: lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-NORECOVER: catch:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont7:
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @__cxa_rethrow() #[[ATTR9]]
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]]
+// CHECK-SANITIZE-NORECOVER: lpad6:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: cleanup
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: invoke void @__cxa_end_catch()
+// CHECK-SANITIZE-NORECOVER-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-NORECOVER: invoke.cont8:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[EH_RESUME:%.*]]
+// CHECK-SANITIZE-NORECOVER: try.cont:
+// CHECK-SANITIZE-NORECOVER-NEXT: br label [[IF_END9]]
+// CHECK-SANITIZE-NORECOVER: if.end9:
+// CHECK-SANITIZE-NORECOVER-NEXT: ret void
+// CHECK-SANITIZE-NORECOVER: eh.resume:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[EXN10:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-NORECOVER-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-NORECOVER-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } undef, ptr [[EXN10]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: [[LPAD_VAL11:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1
+// CHECK-SANITIZE-NORECOVER-NEXT: resume { ptr, i32 } [[LPAD_VAL11]]
+// CHECK-SANITIZE-NORECOVER: terminate.lpad:
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-NORECOVER-NEXT: catch ptr null
+// CHECK-SANITIZE-NORECOVER-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0
+// CHECK-SANITIZE-NORECOVER-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR7]]
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+// CHECK-SANITIZE-NORECOVER: unreachable:
+// CHECK-SANITIZE-NORECOVER-NEXT: unreachable
+//
+// CHECK-SANITIZE-TRAP: Function Attrs: mustprogress noinline optnone
+// CHECK-SANITIZE-TRAP-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki
+// CHECK-SANITIZE-TRAP-SAME: (i32 noundef [[X:%.*]]) #[[ATTR6:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-SANITIZE-TRAP-NEXT: entry:
+// CHECK-SANITIZE-TRAP-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+// CHECK-SANITIZE-TRAP: if.then:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END]]
+// CHECK-SANITIZE-TRAP: if.end:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]]
+// CHECK-SANITIZE-TRAP: if.then2:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END3]]
+// CHECK-SANITIZE-TRAP: if.end3:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4
+// CHECK-SANITIZE-TRAP-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]]
+// CHECK-SANITIZE-TRAP: if.then5:
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z10will_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[TRY_CONT:%.*]]
+// CHECK-SANITIZE-TRAP: lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: br label [[CATCH:%.*]]
+// CHECK-SANITIZE-TRAP: catch:
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: call void @_Z14will_not_throwv() #[[ATTR8]]
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @_Z11might_throwv()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont7:
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @__cxa_rethrow() #[[ATTR9]]
+// CHECK-SANITIZE-TRAP-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]]
+// CHECK-SANITIZE-TRAP: lpad6:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: cleanup
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0
+// CHECK-SANITIZE-TRAP-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1
+// CHECK-SANITIZE-TRAP-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: invoke void @__cxa_end_catch()
+// CHECK-SANITIZE-TRAP-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]]
+// CHECK-SANITIZE-TRAP: invoke.cont8:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[EH_RESUME:%.*]]
+// CHECK-SANITIZE-TRAP: try.cont:
+// CHECK-SANITIZE-TRAP-NEXT: br label [[IF_END9]]
+// CHECK-SANITIZE-TRAP: if.end9:
+// CHECK-SANITIZE-TRAP-NEXT: ret void
+// CHECK-SANITIZE-TRAP: eh.resume:
+// CHECK-SANITIZE-TRAP-NEXT: [[EXN10:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-SANITIZE-TRAP-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-SANITIZE-TRAP-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } undef, ptr [[EXN10]], 0
+// CHECK-SANITIZE-TRAP-NEXT: [[LPAD_VAL11:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1
+// CHECK-SANITIZE-TRAP-NEXT: resume { ptr, i32 } [[LPAD_VAL11]]
+// CHECK-SANITIZE-TRAP: terminate.lpad:
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 }
+// CHECK-SANITIZE-TRAP-NEXT: catch ptr null
+// CHECK-SANITIZE-TRAP-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0
+// CHECK-SANITIZE-TRAP-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR7]]
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+// CHECK-SANITIZE-TRAP: unreachable:
+// CHECK-SANITIZE-TRAP-NEXT: unreachable
+//
+void exception_escape_is_ok(int x) {
+ if (x == 2) {
+#line 100
+ will_throw();
+ }
+#line 200
+ will_not_throw();
+ if (x == 3) {
+#line 300
+ will_throw();
+ }
+ if (x == 4) {
+ try {
+#line 400
+ will_not_throw();
+#line 500
+ will_throw();
+ } catch (...) {
+#line 600
+ will_not_throw();
+#line 700
+ might_throw();
+#line 800
+ throw;
+ }
+ }
+}
Index: clang/lib/Driver/SanitizerArgs.cpp
===================================================================
--- clang/lib/Driver/SanitizerArgs.cpp
+++ clang/lib/Driver/SanitizerArgs.cpp
@@ -58,8 +58,9 @@
SanitizerKind::Undefined | SanitizerKind::Integer |
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
-static const SanitizerMask Unrecoverable =
- SanitizerKind::Unreachable | SanitizerKind::Return;
+static const SanitizerMask Unrecoverable = SanitizerKind::Unreachable |
+ SanitizerKind::Return |
+ SanitizerKind::ExceptionEscape;
static const SanitizerMask AlwaysRecoverable = SanitizerKind::KernelAddress |
SanitizerKind::KernelHWAddress |
SanitizerKind::KCFI;
Index: clang/lib/CodeGen/EHScopeStack.h
===================================================================
--- clang/lib/CodeGen/EHScopeStack.h
+++ clang/lib/CodeGen/EHScopeStack.h
@@ -342,6 +342,12 @@
/// Pops a terminate handler off the stack.
void popTerminate();
+ /// Push a UB handler on the stack.
+ void pushUB();
+
+ /// Pops a UB handler off the stack.
+ void popUB();
+
// Returns true iff the current scope is either empty or contains only
// lifetime markers, i.e. no real cleanup code
bool containsOnlyLifetimeMarkers(stable_iterator Old) const;
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -134,7 +134,8 @@
SANITIZER_CHECK(SubOverflow, sub_overflow, 0) \
SANITIZER_CHECK(TypeMismatch, type_mismatch, 1) \
SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0) \
- SANITIZER_CHECK(VLABoundNotPositive, vla_bound_not_positive, 0)
+ SANITIZER_CHECK(VLABoundNotPositive, vla_bound_not_positive, 0) \
+ SANITIZER_CHECK(ExceptionEscape, exception_escape, 0)
enum SanitizerHandler {
#define SANITIZER_CHECK(Enum, Name, Version) Enum,
@@ -1959,11 +1960,19 @@
llvm::BasicBlock *TerminateLandingPad = nullptr;
llvm::BasicBlock *TerminateHandler = nullptr;
+
+ llvm::BasicBlock *ExceptionEscapeUBSanitizerBB = nullptr;
+ llvm::BasicBlock *ExceptionEscapeUBLandingPad = nullptr;
+ llvm::BasicBlock *ExceptionEscapeUBHandler = nullptr;
+
llvm::SmallVector<llvm::BasicBlock *, 2> TrapBBs;
/// Terminate funclets keyed by parent funclet pad.
llvm::MapVector<llvm::Value *, llvm::BasicBlock *> TerminateFunclets;
+ llvm::BasicBlock *UBLandingPad = nullptr;
+ llvm::AllocaInst *ExceptionEscapeUBLastInvokeSrcLoc = nullptr;
+
/// Largest vector width used in ths function. Will be used to create a
/// function attribute.
unsigned LargestVectorWidth = 0;
@@ -2382,15 +2391,29 @@
/// Emit a test that checks if the return value \p RV is nonnull.
void EmitReturnValueCheck(llvm::Value *RV);
+ /// Internal to `EmitStartEHSpec()`, do not use directly.
+ bool StartEHSpec_ExceptionEscapeIsProgramTermination(const Decl *D);
+
/// EmitStartEHSpec - Emit the start of the exception spec.
void EmitStartEHSpec(const Decl *D);
+ /// Internal to `EmitEndEHSpec()`, do not use directly.
+ bool EndEHSpec_ExceptionEscapeIsProgramTermination(const Decl *D);
+
/// EmitEndEHSpec - Emit the end of the exception spec.
void EmitEndEHSpec(const Decl *D);
+ /// getExceptionEscapeUBLandingPad - Return a simple basic block
+ /// that just calls the ubsan handler, if enabled.
+ llvm::BasicBlock *getExceptionEscapeUBSanitizerBB();
+
/// getTerminateLandingPad - Return a landing pad that just calls terminate.
llvm::BasicBlock *getTerminateLandingPad();
+ /// getExceptionEscapeUBLandingPad - Return a landing pad that just calls
+ /// ubsan handler, if enabled.
+ llvm::BasicBlock *getExceptionEscapeUBLandingPad();
+
/// getTerminateLandingPad - Return a cleanup funclet that just calls
/// terminate.
llvm::BasicBlock *getTerminateFunclet();
@@ -2400,6 +2423,11 @@
/// a terminate scope encloses a try.
llvm::BasicBlock *getTerminateHandler();
+ /// getExceptionEscapeUBHandler - Return a handler (not a landing pad, just
+ /// a catch handler) that just calls ubsan check, if it is enabled.
+ /// This is used when a UB scope encloses a try.
+ llvm::BasicBlock *getExceptionEscapeUBHandler();
+
llvm::Type *ConvertTypeForMem(QualType T);
llvm::Type *ConvertType(QualType T);
llvm::Type *ConvertType(const TypeDecl *T) {
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -446,6 +446,9 @@
EmitIfUsed(*this, TerminateLandingPad);
EmitIfUsed(*this, TerminateHandler);
EmitIfUsed(*this, UnreachableBlock);
+ EmitIfUsed(*this, ExceptionEscapeUBSanitizerBB);
+ EmitIfUsed(*this, ExceptionEscapeUBLandingPad);
+ EmitIfUsed(*this, ExceptionEscapeUBHandler);
for (const auto &FuncletAndParent : TerminateFunclets)
EmitIfUsed(*this, FuncletAndParent.second);
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -3187,7 +3187,8 @@
assert(Kind.countPopulation() == 1);
if (Kind == SanitizerKind::Function || Kind == SanitizerKind::Vptr)
return CheckRecoverableKind::AlwaysRecoverable;
- else if (Kind == SanitizerKind::Return || Kind == SanitizerKind::Unreachable)
+ else if (Kind == SanitizerKind::ExceptionEscape ||
+ Kind == SanitizerKind::Return || Kind == SanitizerKind::Unreachable)
return CheckRecoverableKind::Unrecoverable;
else
return CheckRecoverableKind::Recoverable;
Index: clang/lib/CodeGen/CGException.cpp
===================================================================
--- clang/lib/CodeGen/CGException.cpp
+++ clang/lib/CodeGen/CGException.cpp
@@ -459,22 +459,22 @@
EmitBlock(createBasicBlock("throw.cont"));
}
-void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
- if (!CGM.getLangOpts().CXXExceptions)
- return;
+bool CodeGenFunction::StartEHSpec_ExceptionEscapeIsProgramTermination(
+ const Decl *D) {
+ assert(CGM.getLangOpts().CXXExceptions && "Only for CXX-Exceptions mode.");
const FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(D);
if (!FD) {
// Check if CapturedDecl is nothrow and create terminate scope for it.
if (const CapturedDecl* CD = dyn_cast_or_null<CapturedDecl>(D)) {
if (CD->isNothrow())
- EHStack.pushTerminate();
+ return true;
}
- return;
+ return false;
}
const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>();
if (!Proto)
- return;
+ return false;
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
// In C++17 and later, 'throw()' aka EST_DynamicNone is treated the same way
@@ -485,18 +485,18 @@
// TODO: Revisit exception specifications for the MS ABI. There is a way to
// encode these in an object file but MSVC doesn't do anything with it.
if (getTarget().getCXXABI().isMicrosoft())
- return;
+ return false;
// In Wasm EH we currently treat 'throw()' in the same way as 'noexcept'. In
// case of throw with types, we ignore it and print a warning for now.
// TODO Correctly handle exception specification in Wasm EH
if (CGM.getLangOpts().hasWasmExceptions()) {
if (EST == EST_DynamicNone)
- EHStack.pushTerminate();
+ return true;
else
CGM.getDiags().Report(D->getLocation(),
diag::warn_wasm_dynamic_exception_spec_ignored)
<< FD->getExceptionSpecSourceRange();
- return;
+ return false;
}
// Currently Emscripten EH only handles 'throw()' but not 'throw' with
// types. 'throw()' handling will be done in JS glue code so we don't need
@@ -524,8 +524,31 @@
} else if (Proto->canThrow() == CT_Cannot) {
// noexcept functions are simple terminate scopes.
if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur
- EHStack.pushTerminate();
+ return true;
}
+ return false;
+}
+
+void CodeGenFunction::EmitStartEHSpec(const Decl *D) {
+ if (!CGM.getLangOpts().CXXExceptions)
+ return;
+
+ // Does the EH Specification for the current function mandate that the
+ // exception escaping out of the current function is required to cause program
+ // termination?
+ if (StartEHSpec_ExceptionEscapeIsProgramTermination(D)) {
+ assert(CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) &&
+ "Forgot to manifest nounwind function attribute in LLVM IR.");
+ EHStack.pushTerminate();
+ return;
+ }
+
+ // If it does not mandate function termination, yet the LLVM IR function is
+ // marked as non-unwinding, then exception escape is UB.
+ // Let UBSan handle it from here.
+ if (CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) &&
+ SanOpts.has(SanitizerKind::ExceptionEscape))
+ EHStack.pushUB();
}
/// Emit the dispatch block for a filter scope if necessary.
@@ -566,22 +589,22 @@
CGF.Builder.CreateUnreachable();
}
-void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
- if (!CGM.getLangOpts().CXXExceptions)
- return;
+bool CodeGenFunction::EndEHSpec_ExceptionEscapeIsProgramTermination(
+ const Decl *D) {
+ assert(CGM.getLangOpts().CXXExceptions && "Only for CXX-Exceptions mode.");
const FunctionDecl* FD = dyn_cast_or_null<FunctionDecl>(D);
if (!FD) {
// Check if CapturedDecl is nothrow and pop terminate scope for it.
if (const CapturedDecl* CD = dyn_cast_or_null<CapturedDecl>(D)) {
if (CD->isNothrow() && !EHStack.empty())
- EHStack.popTerminate();
+ return true;
}
- return;
+ return false;
}
const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>();
if (!Proto)
- return;
+ return false;
ExceptionSpecificationType EST = Proto->getExceptionSpecType();
if (EST == EST_Dynamic ||
@@ -589,14 +612,14 @@
// TODO: Revisit exception specifications for the MS ABI. There is a way to
// encode these in an object file but MSVC doesn't do anything with it.
if (getTarget().getCXXABI().isMicrosoft())
- return;
+ return false;
// In wasm we currently treat 'throw()' in the same way as 'noexcept'. In
// case of throw with types, we ignore it and print a warning for now.
// TODO Correctly handle exception specification in wasm
if (CGM.getLangOpts().hasWasmExceptions()) {
if (EST == EST_DynamicNone)
- EHStack.popTerminate();
- return;
+ return true;
+ return false;
}
EHFilterScope &filterScope = cast<EHFilterScope>(*EHStack.begin());
emitFilterDispatchBlock(*this, filterScope);
@@ -604,8 +627,32 @@
} else if (Proto->canThrow() == CT_Cannot &&
/* possible empty when under async exceptions */
!EHStack.empty()) {
- EHStack.popTerminate();
+ return true;
}
+
+ return false;
+}
+
+void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
+ if (!CGM.getLangOpts().CXXExceptions)
+ return;
+
+ // Does the EH Specification for the current function mandate that the
+ // exception escaping out of the current function is required to cause program
+ // termination?
+ if (EndEHSpec_ExceptionEscapeIsProgramTermination(D)) {
+ assert(CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) &&
+ "Forgot to manifest nounwind function attribute in LLVM IR.");
+ EHStack.popTerminate();
+ return;
+ }
+
+ // If it does not mandate function termination, yet the LLVM IR function is
+ // marked as non-unwinding, then exception escape is UB.
+ // Let UBSan handle it from here.
+ if (CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) &&
+ SanOpts.has(SanitizerKind::ExceptionEscape))
+ EHStack.popUB();
}
void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) {
@@ -692,6 +739,10 @@
case EHScope::Terminate:
dispatchBlock = getTerminateHandler();
break;
+
+ case EHScope::UB:
+ dispatchBlock = getExceptionEscapeUBHandler();
+ break;
}
scope.setCachedEHDispatchBlock(dispatchBlock);
}
@@ -733,6 +784,10 @@
case EHScope::Terminate:
DispatchBlock->setName("terminate");
break;
+
+ case EHScope::UB:
+ DispatchBlock->setName("ub");
+ break;
}
EHS.setCachedEHDispatchBlock(DispatchBlock);
return DispatchBlock;
@@ -748,6 +803,7 @@
case EHScope::Filter:
case EHScope::Catch:
case EHScope::Terminate:
+ case EHScope::UB:
return false;
}
@@ -813,6 +869,8 @@
switch (innermostEHScope.getKind()) {
case EHScope::Terminate:
return getTerminateLandingPad();
+ case EHScope::UB:
+ return getExceptionEscapeUBLandingPad();
case EHScope::Catch:
case EHScope::Cleanup:
@@ -872,7 +930,8 @@
}
case EHScope::Terminate:
- // Terminate scopes are basically catch-alls.
+ case EHScope::UB:
+ // Terminate/UB scopes are basically catch-alls.
assert(!hasCatchAll);
hasCatchAll = true;
goto done;
@@ -943,7 +1002,7 @@
Builder.restoreIP(savedIP);
return lpad;
-}
+ }
static void emitCatchPadBlock(CodeGenFunction &CGF, EHCatchScope &CatchScope) {
llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock();
@@ -1601,6 +1660,120 @@
return TerminateFunclet;
}
+llvm::BasicBlock *CodeGenFunction::getExceptionEscapeUBSanitizerBB() {
+ assert(SanOpts.has(SanitizerKind::ExceptionEscape) &&
+ "Should only get here if the Exception Escape Sanitizer is enabled.");
+
+ if (ExceptionEscapeUBSanitizerBB)
+ return ExceptionEscapeUBSanitizerBB;
+
+ SanitizerScope SanScope(this);
+
+ CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
+
+ // This will get inserted at the end of the function.
+ ExceptionEscapeUBSanitizerBB =
+ createBasicBlock("exception-escape.sanitization");
+ Builder.SetInsertPoint(ExceptionEscapeUBSanitizerBB);
+
+ llvm::Value *Exn = getExceptionFromSlot();
+
+ llvm::Type *SourceLocationTy =
+ EmitCheckSourceLocation(SourceLocation())->getType();
+ ExceptionEscapeUBLastInvokeSrcLoc =
+ CreateTempAlloca(SourceLocationTy, "invoke.srcloc");
+ // The callers are responsible with populating this with relevant information.
+
+ llvm::Value *InvokeSrcLoc = Builder.CreateLoad(
+ Address(ExceptionEscapeUBLastInvokeSrcLoc, SourceLocationTy,
+ CharUnits::fromQuantity(
+ ExceptionEscapeUBLastInvokeSrcLoc->getAlign().value())),
+ "curr.invoke.srcloc");
+
+ llvm::Value *DynamicData[] = {InvokeSrcLoc, Exn};
+ // FIXME: can we do anything interesting with `Exn`?
+ EmitCheck({std::make_pair(llvm::ConstantInt::getFalse(getLLVMContext()),
+ SanitizerKind::ExceptionEscape)},
+ SanitizerHandler::ExceptionEscape, /*StaticData=*/{}, DynamicData);
+
+ Builder.CreateUnreachable();
+
+ // Restore the saved insertion state.
+ Builder.restoreIP(SavedIP);
+
+ return ExceptionEscapeUBSanitizerBB;
+}
+
+llvm::BasicBlock *CodeGenFunction::getExceptionEscapeUBLandingPad() {
+ assert(SanOpts.has(SanitizerKind::ExceptionEscape) &&
+ "Should only get here if the Exception Escape Sanitizer is enabled.");
+
+ if (ExceptionEscapeUBLandingPad)
+ return ExceptionEscapeUBLandingPad;
+
+ llvm::BasicBlock *SanBB = getExceptionEscapeUBSanitizerBB();
+
+ SanitizerScope SanScope(this);
+
+ CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
+
+ // This will get inserted at the end of the function.
+ ExceptionEscapeUBLandingPad = createBasicBlock("exception-escape.lpad");
+ Builder.SetInsertPoint(ExceptionEscapeUBLandingPad);
+
+ // Tell the backend that this is a landing pad.
+ const EHPersonality &Personality = EHPersonality::get(*this);
+
+ if (!CurFn->hasPersonalityFn())
+ CurFn->setPersonalityFn(getOpaquePersonalityFn(CGM, Personality));
+
+ llvm::LandingPadInst *LPadInst =
+ Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty), 0);
+ LPadInst->addClause(getCatchAllValue(*this));
+
+ llvm::Value *Exn = nullptr;
+ if (getLangOpts().CPlusPlus)
+ Exn = Builder.CreateExtractValue(LPadInst, 0);
+ Builder.CreateStore(Exn, getExceptionSlot());
+
+ // And just fall through into the actual sanitizer block.
+ Builder.CreateBr(SanBB);
+
+ // Restore the saved insertion state.
+ Builder.restoreIP(SavedIP);
+
+ return ExceptionEscapeUBLandingPad;
+}
+
+llvm::BasicBlock *CodeGenFunction::getExceptionEscapeUBHandler() {
+ assert(SanOpts.has(SanitizerKind::ExceptionEscape) &&
+ "Should only get here if the Exception Escape Sanitizer is enabled.");
+
+ if (ExceptionEscapeUBHandler)
+ return ExceptionEscapeUBHandler;
+
+ llvm::BasicBlock *SanBB = getExceptionEscapeUBSanitizerBB();
+
+ // Set up the UB handler. This block is inserted at the very
+ // end of the function by FinishFunction.
+ ExceptionEscapeUBHandler = createBasicBlock("exception-escape.handler");
+ CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP();
+ Builder.SetInsertPoint(ExceptionEscapeUBHandler);
+
+ // For C++, the exception is already in the slot, otherwise we need to store
+ // null.
+ if (!getLangOpts().CPlusPlus)
+ Builder.CreateStore(/*Exn=*/nullptr, getExceptionSlot());
+
+ // And just fall through into the actual sanitizer block.
+ Builder.CreateBr(SanBB);
+
+ // Restore the saved insertion state.
+ Builder.restoreIP(SavedIP);
+
+ return ExceptionEscapeUBHandler;
+}
+
llvm::BasicBlock *CodeGenFunction::getEHResumeBlock(bool isCleanup) {
if (EHResumeBlock) return EHResumeBlock;
Index: clang/lib/CodeGen/CGCleanup.h
===================================================================
--- clang/lib/CodeGen/CGCleanup.h
+++ clang/lib/CodeGen/CGCleanup.h
@@ -101,7 +101,7 @@
};
public:
- enum Kind { Cleanup, Catch, Terminate, Filter };
+ enum Kind { Cleanup, Catch, Terminate, UB, Filter };
EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)
: CachedLandingPad(nullptr), CachedEHDispatchBlock(nullptr),
@@ -487,6 +487,16 @@
}
};
+/// An exceptions scope which causes UB if any exception reaches it.
+class EHUBScope : public EHScope {
+public:
+ EHUBScope(EHScopeStack::stable_iterator enclosingEHScope)
+ : EHScope(UB, enclosingEHScope) {}
+ static size_t getSize() { return sizeof(EHUBScope); }
+
+ static bool classof(const EHScope *scope) { return scope->getKind() == UB; }
+};
+
/// A non-stable pointer into the scope stack.
class EHScopeStack::iterator {
char *Ptr;
@@ -524,6 +534,10 @@
case EHScope::Terminate:
Size = EHTerminateScope::getSize();
break;
+
+ case EHScope::UB:
+ Size = EHUBScope::getSize();
+ break;
}
Ptr += llvm::alignTo(Size, ScopeStackAlignment);
return *this;
@@ -572,6 +586,14 @@
deallocate(EHTerminateScope::getSize());
}
+inline void EHScopeStack::popUB() {
+ assert(!empty() && "popping exception stack when not empty");
+
+ EHUBScope &scope = cast<EHUBScope>(*begin());
+ InnermostEHScope = scope.getEnclosingEHScope();
+ deallocate(EHUBScope::getSize());
+}
+
inline EHScopeStack::iterator EHScopeStack::find(stable_iterator sp) const {
assert(sp.isValid() && "finding invalid savepoint");
assert(sp.Size <= stable_begin().Size && "finding savepoint after pop");
Index: clang/lib/CodeGen/CGCleanup.cpp
===================================================================
--- clang/lib/CodeGen/CGCleanup.cpp
+++ clang/lib/CodeGen/CGCleanup.cpp
@@ -270,6 +270,12 @@
InnermostEHScope = stable_begin();
}
+void EHScopeStack::pushUB() {
+ char *Buffer = allocate(EHUBScope::getSize());
+ new (Buffer) EHUBScope(InnermostEHScope);
+ InnermostEHScope = stable_begin();
+}
+
/// Remove any 'null' fixups on the stack. However, we can't pop more
/// fixups than the fixup depth on the innermost normal cleanup, or
/// else fixups that we try to add to that cleanup will end up in the
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -5399,6 +5399,16 @@
if (!InvokeDest) {
CI = Builder.CreateCall(IRFuncTy, CalleePtr, IRCallArgs, BundleList);
} else {
+ if (SanOpts.has(SanitizerKind::ExceptionEscape) &&
+ ExceptionEscapeUBLastInvokeSrcLoc) {
+ llvm::Constant *CheckSourceLocation = EmitCheckSourceLocation(Loc);
+ Builder.CreateStore(
+ CheckSourceLocation,
+ Address(ExceptionEscapeUBLastInvokeSrcLoc,
+ CheckSourceLocation->getType(),
+ CharUnits::fromQuantity(
+ ExceptionEscapeUBLastInvokeSrcLoc->getAlign().value())));
+ }
llvm::BasicBlock *Cont = createBasicBlock("invoke.cont");
CI = Builder.CreateInvoke(IRFuncTy, CalleePtr, Cont, InvokeDest, IRCallArgs,
BundleList);
Index: clang/include/clang/Basic/Sanitizers.def
===================================================================
--- clang/include/clang/Basic/Sanitizers.def
+++ clang/include/clang/Basic/Sanitizers.def
@@ -107,6 +107,7 @@
SANITIZER("unreachable", Unreachable)
SANITIZER("vla-bound", VLABound)
SANITIZER("vptr", Vptr)
+SANITIZER("exception-escape", ExceptionEscape)
// IntegerSanitizer
SANITIZER("unsigned-integer-overflow", UnsignedIntegerOverflow)
@@ -144,7 +145,7 @@
IntegerDivideByZero | NonnullAttribute | Null | ObjectSize |
PointerOverflow | Return | ReturnsNonnullAttribute | Shift |
SignedIntegerOverflow | Unreachable | VLABound | Function |
- Vptr)
+ Vptr | ExceptionEscape)
// -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
Index: clang/docs/UndefinedBehaviorSanitizer.rst
===================================================================
--- clang/docs/UndefinedBehaviorSanitizer.rst
+++ clang/docs/UndefinedBehaviorSanitizer.rst
@@ -180,6 +180,8 @@
Incompatible with ``-fno-rtti``. Link must be performed by ``clang++``, not
``clang``, to make sure C++-specific parts of the runtime library and C++
standard libraries are present.
+ - ``-fsanitize=exception-escape``: A C++ exception unwinding out of an
+ non-unwind function is an undefined behavior. This catches such situations.
You can also use the following check groups:
- ``-fsanitize=undefined``: All of the checks listed above other than
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -797,8 +797,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.
@@ -818,6 +818,11 @@
returning uninitialized variables from functions is more aggressively
reported. ``-fno-sanitize-memory-param-retval`` restores the previous
behavior.
+- A new Undefined Behavior Sanitizer check has been implemented:
+ ``-fsanitize=exception-escape`` (part of ``-fsanitize=undefined``),
+ which catches cases of C++ exceptions trying to unwind
+ out of non-unwindable functions.
+
Core Analysis Improvements
==========================
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits