lebedev.ri created this revision. lebedev.ri added reviewers: aaron.ballman, rjmccall, efriedma, jcranmer, jyknight, rsmith. lebedev.ri added a project: LLVM. Herald added subscribers: StephenFan, dschuff. Herald added a project: All. lebedev.ri requested review of this revision. Herald added subscribers: MaskRay, aheejin. Herald added a project: clang.
This tentatively implements the following RFC: https://discourse.llvm.org/t/rfc-clang-true-noexcept-aka-defaults-are-often-wrong-hardcoded-defaults-are-always-wrong/67629 The idea is that when the opt-in is specified, we turn all EH Terminate scopes into EH UB scopes, and don't emit any `std::terminate()` calls, which is the user-facing change. This probably needs better docs, and might be missing some pieces. We might be able to do more to omit more destructor calls. This is missing UBSan bits, those will be in D137381 <https://reviews.llvm.org/D137381>. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D141561 Files: clang/docs/ReleaseNotes.rst clang/docs/UsersManual.rst clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Driver/Options.td clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CGCleanup.cpp clang/lib/CodeGen/CGCleanup.h clang/lib/CodeGen/CGException.cpp clang/lib/CodeGen/CodeGenFunction.h clang/lib/CodeGen/EHScopeStack.h clang/lib/Driver/ToolChains/Clang.cpp clang/test/CodeGenCXX/exception-escape-as-ub-cleanups.cpp clang/test/CodeGenCXX/exception-escape.cpp
Index: clang/test/CodeGenCXX/exception-escape.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/exception-escape.cpp @@ -0,0 +1,632 @@ +// 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-ALL,CHECK-NO-EXCEPTIONS +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS,CHECK-NORMAL-NOEXCEPT +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions -fstrict-noexcept | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS,CHECK-STRICT-NOEXCEPT + +void will_throw(int line = __builtin_LINE()); +void might_throw(int line = __builtin_LINE()); +void will_not_throw(int line = __builtin_LINE()) noexcept; + +//. +// CHECK-ALL: @_ZTIi = external constant ptr +//. +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_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_throwi(i32 noundef 100) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]] +// CHECK-NO-EXCEPTIONS: if.end: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR3:[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_throwi(i32 noundef 300) +// 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_throwi(i32 noundef 400) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 500) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END6]] +// CHECK-NO-EXCEPTIONS: if.end6: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP3:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP7:%.*]] = icmp eq i32 [[TMP3]], 5 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP7]], label [[IF_THEN8:%.*]], label [[IF_END9:%.*]] +// CHECK-NO-EXCEPTIONS: if.then8: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 900) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END9]] +// CHECK-NO-EXCEPTIONS: if.end9: +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-NORMAL-NOEXCEPT: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NORMAL-NOEXCEPT-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_ubi +// CHECK-NORMAL-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-NORMAL-NOEXCEPT-NEXT: entry: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X15:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 100) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-NORMAL-NOEXCEPT: if.end: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR6:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then2: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 300) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT3:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont3: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END4]] +// CHECK-NORMAL-NOEXCEPT: if.end4: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END9:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then6: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 400) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 500) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont7: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: catch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 600) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 700) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont8: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR7:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: try.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END9]] +// CHECK-NORMAL-NOEXCEPT: if.end9: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP7:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP10:%.*]] = icmp eq i32 [[TMP7]], 5 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP10]], label [[IF_THEN11:%.*]], label [[IF_END18:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then11: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 900) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT13:%.*]] unwind label [[LPAD12:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont13: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT17:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad12: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP8:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr @_ZTIi +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP9]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP10:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP10]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH_DISPATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: catch.dispatch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP11:%.*]] = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[MATCHES:%.*]] = icmp eq i32 [[SEL]], [[TMP11]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[MATCHES]], label [[CATCH14:%.*]], label [[TERMINATE_HANDLER:%.*]] +// CHECK-NORMAL-NOEXCEPT: catch14: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN16:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP12:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN16]]) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP13]], ptr [[X15]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP14:%.*]] = load i32, ptr [[X15]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP14]], ptr [[EXCEPTION]], align 16 +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR7]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: try.cont17: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END18]] +// CHECK-NORMAL-NOEXCEPT: if.end18: +// CHECK-NORMAL-NOEXCEPT-NEXT: ret void +// CHECK-NORMAL-NOEXCEPT: terminate.lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP15:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP16:%.*]] = extractvalue { ptr, i32 } [[TMP15]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP16]]) #[[ATTR8:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// CHECK-NORMAL-NOEXCEPT: terminate.handler: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN19:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[EXN19]]) #[[ATTR8]] +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// CHECK-NORMAL-NOEXCEPT: unreachable: +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// +// CHECK-STRICT-NOEXCEPT: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-STRICT-NOEXCEPT-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_ubi +// CHECK-STRICT-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-STRICT-NOEXCEPT-NEXT: entry: +// CHECK-STRICT-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[X12:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 100) #[[ATTR6:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-STRICT-NOEXCEPT: if.end: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then2: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 300) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END3]] +// CHECK-STRICT-NOEXCEPT: if.end3: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END6:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then5: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 400) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 500) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: catch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 600) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z11might_throwi(i32 noundef 700) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__cxa_rethrow() #[[ATTR7:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// CHECK-STRICT-NOEXCEPT: try.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END6]] +// CHECK-STRICT-NOEXCEPT: if.end6: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP7:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP7:%.*]] = icmp eq i32 [[TMP7]], 5 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP7]], label [[IF_THEN8:%.*]], label [[IF_END15:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then8: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 900) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT10:%.*]] unwind label [[LPAD9:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont10: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT14:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad9: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP8:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr @_ZTIi +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP9]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP10:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP10]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH_DISPATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: catch.dispatch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP11:%.*]] = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[MATCHES:%.*]] = icmp eq i32 [[SEL]], [[TMP11]] +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[MATCHES]], label [[CATCH11:%.*]], label [[UNREACHABLE:%.*]] +// CHECK-STRICT-NOEXCEPT: catch11: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN13:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP12:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN13]]) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP12]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP13]], ptr [[X12]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP14:%.*]] = load i32, ptr [[X12]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP14]], ptr [[EXCEPTION]], align 16 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR7]] +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// CHECK-STRICT-NOEXCEPT: try.cont14: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END15]] +// CHECK-STRICT-NOEXCEPT: if.end15: +// CHECK-STRICT-NOEXCEPT-NEXT: ret void +// CHECK-STRICT-NOEXCEPT: unreachable: +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// +void exception_escape_is_program_termination_or_ub(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; + } + } + if (x == 5) { + try { +#line 900 + will_throw(); + } catch (int x) { +#line 1000 + throw x; + } + } +} + +// 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:%.*]]) #[[ATTR0]] { +// 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_throwi(i32 noundef 100) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]] +// CHECK-NO-EXCEPTIONS: if.end: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR3]] +// 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_throwi(i32 noundef 300) +// 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_throwi(i32 noundef 400) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 500) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END6]] +// CHECK-NO-EXCEPTIONS: if.end6: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP3:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP7:%.*]] = icmp eq i32 [[TMP3]], 5 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP7]], label [[IF_THEN8:%.*]], label [[IF_END9:%.*]] +// CHECK-NO-EXCEPTIONS: if.then8: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 900) +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END9]] +// CHECK-NO-EXCEPTIONS: if.end9: +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-NORMAL-NOEXCEPT: Function Attrs: mustprogress noinline optnone +// CHECK-NORMAL-NOEXCEPT-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki +// CHECK-NORMAL-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-NORMAL-NOEXCEPT-NEXT: entry: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X15:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 100) +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-NORMAL-NOEXCEPT: if.end: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then2: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 300) +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END3]] +// CHECK-NORMAL-NOEXCEPT: if.end3: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then5: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 400) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 500) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: catch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 600) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 700) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont7: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR7]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]] +// CHECK-NORMAL-NOEXCEPT: lpad6: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: cleanup +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_end_catch() +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont8: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[EH_RESUME:%.*]] +// CHECK-NORMAL-NOEXCEPT: try.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END9]] +// CHECK-NORMAL-NOEXCEPT: if.end9: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP10:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP10:%.*]] = icmp eq i32 [[TMP10]], 5 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP10]], label [[IF_THEN11:%.*]], label [[IF_END19:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then11: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 900) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT13:%.*]] unwind label [[LPAD12:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont13: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT18:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad12: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP11:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr @_ZTIi +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP12:%.*]] = extractvalue { ptr, i32 } [[TMP11]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP12]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP11]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP13]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH_DISPATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: catch.dispatch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP14:%.*]] = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[MATCHES:%.*]] = icmp eq i32 [[SEL]], [[TMP14]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[MATCHES]], label [[CATCH14:%.*]], label [[EH_RESUME]] +// CHECK-NORMAL-NOEXCEPT: catch14: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN16:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP15:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN16]]) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP15]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP16]], ptr [[X15]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP17:%.*]] = load i32, ptr [[X15]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP17]], ptr [[EXCEPTION]], align 16 +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR7]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE]] unwind label [[LPAD17:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad17: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP18:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: cleanup +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP19:%.*]] = extractvalue { ptr, i32 } [[TMP18]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP19]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP20:%.*]] = extractvalue { ptr, i32 } [[TMP18]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP20]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__cxa_end_catch() #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-NORMAL-NOEXCEPT: try.cont18: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END19]] +// CHECK-NORMAL-NOEXCEPT: if.end19: +// CHECK-NORMAL-NOEXCEPT-NEXT: ret void +// CHECK-NORMAL-NOEXCEPT: eh.resume: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN20:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[SEL21:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN20]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[LPAD_VAL22:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL21]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: resume { ptr, i32 } [[LPAD_VAL22]] +// CHECK-NORMAL-NOEXCEPT: terminate.lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP21:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP22:%.*]] = extractvalue { ptr, i32 } [[TMP21]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP22]]) #[[ATTR8]] +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// CHECK-NORMAL-NOEXCEPT: unreachable: +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// +// CHECK-STRICT-NOEXCEPT: Function Attrs: mustprogress noinline optnone +// CHECK-STRICT-NOEXCEPT-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki +// CHECK-STRICT-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-STRICT-NOEXCEPT-NEXT: entry: +// CHECK-STRICT-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[X15:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 2 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 100) +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-STRICT-NOEXCEPT: if.end: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 200) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 3 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END3:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then2: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 300) +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END3]] +// CHECK-STRICT-NOEXCEPT: if.end3: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP4:%.*]] = icmp eq i32 [[TMP2]], 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP4]], label [[IF_THEN5:%.*]], label [[IF_END9:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then5: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 400) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 500) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP3:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: catch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP6:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z14will_not_throwi(i32 noundef 600) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 700) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont7: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR7]] +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD6]] +// CHECK-STRICT-NOEXCEPT: lpad6: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP7:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: cleanup +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP8]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP7]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP9]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @__cxa_end_catch() +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT8:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont8: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[EH_RESUME:%.*]] +// CHECK-STRICT-NOEXCEPT: try.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END9]] +// CHECK-STRICT-NOEXCEPT: if.end9: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP10:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP10:%.*]] = icmp eq i32 [[TMP10]], 5 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP10]], label [[IF_THEN11:%.*]], label [[IF_END19:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then11: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 900) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT13:%.*]] unwind label [[LPAD12:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont13: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT18:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad12: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP11:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr @_ZTIi +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP12:%.*]] = extractvalue { ptr, i32 } [[TMP11]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP12]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP11]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP13]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH_DISPATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: catch.dispatch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP14:%.*]] = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[MATCHES:%.*]] = icmp eq i32 [[SEL]], [[TMP14]] +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[MATCHES]], label [[CATCH14:%.*]], label [[EH_RESUME]] +// CHECK-STRICT-NOEXCEPT: catch14: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN16:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP15:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN16]]) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP15]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP16]], ptr [[X15]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXCEPTION:%.*]] = call ptr @__cxa_allocate_exception(i64 4) #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP17:%.*]] = load i32, ptr [[X15]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP17]], ptr [[EXCEPTION]], align 16 +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @__cxa_throw(ptr [[EXCEPTION]], ptr @_ZTIi, ptr null) #[[ATTR7]] +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[UNREACHABLE]] unwind label [[LPAD17:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad17: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP18:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: cleanup +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP19:%.*]] = extractvalue { ptr, i32 } [[TMP18]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP19]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP20:%.*]] = extractvalue { ptr, i32 } [[TMP18]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP20]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__cxa_end_catch() #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-STRICT-NOEXCEPT: try.cont18: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END19]] +// CHECK-STRICT-NOEXCEPT: if.end19: +// CHECK-STRICT-NOEXCEPT-NEXT: ret void +// CHECK-STRICT-NOEXCEPT: eh.resume: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN20:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[SEL21:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN20]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: [[LPAD_VAL22:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL21]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: resume { ptr, i32 } [[LPAD_VAL22]] +// CHECK-STRICT-NOEXCEPT: terminate.lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP21:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP22:%.*]] = extractvalue { ptr, i32 } [[TMP21]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP22]]) #[[ATTR8:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// CHECK-STRICT-NOEXCEPT: unreachable: +// CHECK-STRICT-NOEXCEPT-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; + } + } + if (x == 5) { + try { +#line 900 + will_throw(); + } catch (int x) { +#line 1000 + throw x; + } + } +} +//. +// CHECK-NO-EXCEPTIONS: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #2 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #3 = { nounwind } +//. +// CHECK-NORMAL-NOEXCEPT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #2 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #3 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #4 = { nounwind memory(none) } +// CHECK-NORMAL-NOEXCEPT: attributes #5 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #6 = { nounwind } +// CHECK-NORMAL-NOEXCEPT: attributes #7 = { noreturn } +// CHECK-NORMAL-NOEXCEPT: attributes #8 = { noreturn nounwind } +//. +// CHECK-STRICT-NOEXCEPT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #2 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #3 = { nounwind memory(none) } +// CHECK-STRICT-NOEXCEPT: attributes #4 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #5 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #6 = { nounwind } +// CHECK-STRICT-NOEXCEPT: attributes #7 = { noreturn } +// CHECK-STRICT-NOEXCEPT: attributes #8 = { noreturn nounwind } +//. +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// CHECK-ALL: {{.*}} +// CHECK-EXCEPTIONS: {{.*}} Index: clang/test/CodeGenCXX/exception-escape-as-ub-cleanups.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/exception-escape-as-ub-cleanups.cpp @@ -0,0 +1,550 @@ +// 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-ALL,CHECK-NO-EXCEPTIONS +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS,CHECK-NORMAL-NOEXCEPT +// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu -fcxx-exceptions -fexceptions -fstrict-noexcept | FileCheck %s --check-prefixes=CHECK-ALL,CHECK-EXCEPTIONS,CHECK-STRICT-NOEXCEPT + +void will_throw(int line = __builtin_LINE()); +void might_throw(int line = __builtin_LINE()); +void will_not_throw(int line = __builtin_LINE()) noexcept; + +struct S { + S(int line = __builtin_LINE()); + ~S(); +}; + +// CHECK-NO-EXCEPTIONS: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NO-EXCEPTIONS-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_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: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-NO-EXCEPTIONS-NEXT: [[STATE3:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NO-EXCEPTIONS-NEXT: [[STATE7:%.*]] = alloca [[STRUCT_S]], align 1 +// 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]], 0 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NO-EXCEPTIONS: if.then: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z11might_throwi(i32 noundef 200) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR3:[0-9]+]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]] +// CHECK-NO-EXCEPTIONS: if.end: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 1 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]] +// CHECK-NO-EXCEPTIONS: if.then2: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]], i32 noundef 300) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z11might_throwi(i32 noundef 400) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END4]] +// CHECK-NO-EXCEPTIONS: if.end4: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 2 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END8:%.*]] +// CHECK-NO-EXCEPTIONS: if.then6: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE7]], i32 noundef 600) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 700) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE7]]) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END8]] +// CHECK-NO-EXCEPTIONS: if.end8: +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-NORMAL-NOEXCEPT: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-NORMAL-NOEXCEPT-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_ubi +// CHECK-NORMAL-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-NORMAL-NOEXCEPT-NEXT: entry: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE4:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE11:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 200) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT1:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont1: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-NORMAL-NOEXCEPT: if.end: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP2:%.*]] = icmp eq i32 [[TMP1]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP2]], label [[IF_THEN3:%.*]], label [[IF_END8:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then3: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE4]], i32 noundef 300) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT5:%.*]] unwind label [[LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont5: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 400) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont7: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE4]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP2:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP2]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP3]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP2]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP4]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad6: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP5:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP6]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP7]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE4]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH]] +// CHECK-NORMAL-NOEXCEPT: catch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP8:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR6:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: try.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END8]] +// CHECK-NORMAL-NOEXCEPT: if.end8: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP9:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP9:%.*]] = icmp eq i32 [[TMP9]], 2 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP9]], label [[IF_THEN10:%.*]], label [[IF_END14:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then10: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE11]], i32 noundef 600) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT12:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont12: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 700) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT13:%.*]] unwind label [[TERMINATE_LPAD]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont13: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE11]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END14]] +// CHECK-NORMAL-NOEXCEPT: if.end14: +// CHECK-NORMAL-NOEXCEPT-NEXT: ret void +// CHECK-NORMAL-NOEXCEPT: terminate.lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP10:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i32 } [[TMP10]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP11]]) #[[ATTR7:[0-9]+]] +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// CHECK-NORMAL-NOEXCEPT: unreachable: +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// +// CHECK-STRICT-NOEXCEPT: Function Attrs: mustprogress noinline nounwind optnone +// CHECK-STRICT-NOEXCEPT-LABEL: define {{[^@]+}}@_Z45exception_escape_is_program_termination_or_ubi +// CHECK-STRICT-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-STRICT-NOEXCEPT-NEXT: entry: +// CHECK-STRICT-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE3:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE9:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) #[[ATTR5:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z11might_throwi(i32 noundef 200) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-STRICT-NOEXCEPT: if.end: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END6:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then2: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]], i32 noundef 300) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 400) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT5:%.*]] unwind label [[LPAD4:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont5: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP2:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP2]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP3]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP2]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP4]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad4: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP5:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP6]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP7]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH]] +// CHECK-STRICT-NOEXCEPT: catch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP8:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__cxa_rethrow() #[[ATTR6:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// CHECK-STRICT-NOEXCEPT: try.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END6]] +// CHECK-STRICT-NOEXCEPT: if.end6: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP9:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP7:%.*]] = icmp eq i32 [[TMP9]], 2 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP7]], label [[IF_THEN8:%.*]], label [[IF_END10:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then8: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE9]], i32 noundef 600) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_Z10will_throwi(i32 noundef 700) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE9]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END10]] +// CHECK-STRICT-NOEXCEPT: if.end10: +// CHECK-STRICT-NOEXCEPT-NEXT: ret void +// +void exception_escape_is_program_termination_or_ub(int x) noexcept { + if(x == 0) { +#line 100 + S state; +#line 200 + might_throw(); + } + if(x == 1) { + try { +#line 300 + S state; +#line 400 + might_throw(); + } catch (...) { +#line 500 + throw; + } + } + if(x == 2) { +#line 600 + S state; +#line 700 + will_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:%.*]]) #[[ATTR0]] { +// CHECK-NO-EXCEPTIONS-NEXT: entry: +// CHECK-NO-EXCEPTIONS-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-NO-EXCEPTIONS-NEXT: [[STATE3:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NO-EXCEPTIONS-NEXT: [[STATE7:%.*]] = alloca [[STRUCT_S]], align 1 +// 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]], 0 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NO-EXCEPTIONS: if.then: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z11might_throwi(i32 noundef 200) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END]] +// CHECK-NO-EXCEPTIONS: if.end: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP1]], 1 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]] +// CHECK-NO-EXCEPTIONS: if.then2: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]], i32 noundef 300) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z11might_throwi(i32 noundef 400) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END4]] +// CHECK-NO-EXCEPTIONS: if.end4: +// CHECK-NO-EXCEPTIONS-NEXT: [[TMP2:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NO-EXCEPTIONS-NEXT: [[CMP5:%.*]] = icmp eq i32 [[TMP2]], 2 +// CHECK-NO-EXCEPTIONS-NEXT: br i1 [[CMP5]], label [[IF_THEN6:%.*]], label [[IF_END8:%.*]] +// CHECK-NO-EXCEPTIONS: if.then6: +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE7]], i32 noundef 600) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_Z10will_throwi(i32 noundef 700) +// CHECK-NO-EXCEPTIONS-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE7]]) #[[ATTR3]] +// CHECK-NO-EXCEPTIONS-NEXT: br label [[IF_END8]] +// CHECK-NO-EXCEPTIONS: if.end8: +// CHECK-NO-EXCEPTIONS-NEXT: ret void +// +// CHECK-NORMAL-NOEXCEPT: Function Attrs: mustprogress noinline optnone +// CHECK-NORMAL-NOEXCEPT-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki +// CHECK-NORMAL-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-NORMAL-NOEXCEPT-NEXT: entry: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE3:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[STATE13:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 200) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-NORMAL-NOEXCEPT: lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: cleanup +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[EH_RESUME:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.end: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP4:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP4]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END10:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then2: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]], i32 noundef 300) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT5:%.*]] unwind label [[LPAD4:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont5: +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 400) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont7: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad4: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP5:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP6]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP7]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad6: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP8:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP9]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP10:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP10]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[CATCH]] +// CHECK-NORMAL-NOEXCEPT: catch: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP11:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR6]] +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD8:%.*]] +// CHECK-NORMAL-NOEXCEPT: lpad8: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP12:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: cleanup +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP13]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP14:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP14]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @__cxa_end_catch() +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT9:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont9: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-NORMAL-NOEXCEPT: try.cont: +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END10]] +// CHECK-NORMAL-NOEXCEPT: if.end10: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP15:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[CMP11:%.*]] = icmp eq i32 [[TMP15]], 2 +// CHECK-NORMAL-NOEXCEPT-NEXT: br i1 [[CMP11]], label [[IF_THEN12:%.*]], label [[IF_END16:%.*]] +// CHECK-NORMAL-NOEXCEPT: if.then12: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]], i32 noundef 600) +// CHECK-NORMAL-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 700) +// CHECK-NORMAL-NOEXCEPT-NEXT: to label [[INVOKE_CONT15:%.*]] unwind label [[LPAD14:%.*]] +// CHECK-NORMAL-NOEXCEPT: invoke.cont15: +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[IF_END16]] +// CHECK-NORMAL-NOEXCEPT: lpad14: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP16:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: cleanup +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP17:%.*]] = extractvalue { ptr, i32 } [[TMP16]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: store ptr [[TMP17]], ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP18:%.*]] = extractvalue { ptr, i32 } [[TMP16]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: store i32 [[TMP18]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]]) #[[ATTR5]] +// CHECK-NORMAL-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-NORMAL-NOEXCEPT: if.end16: +// CHECK-NORMAL-NOEXCEPT-NEXT: ret void +// CHECK-NORMAL-NOEXCEPT: eh.resume: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[EXN17:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN17]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: [[LPAD_VAL18:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1 +// CHECK-NORMAL-NOEXCEPT-NEXT: resume { ptr, i32 } [[LPAD_VAL18]] +// CHECK-NORMAL-NOEXCEPT: terminate.lpad: +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP19:%.*]] = landingpad { ptr, i32 } +// CHECK-NORMAL-NOEXCEPT-NEXT: catch ptr null +// CHECK-NORMAL-NOEXCEPT-NEXT: [[TMP20:%.*]] = extractvalue { ptr, i32 } [[TMP19]], 0 +// CHECK-NORMAL-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP20]]) #[[ATTR7]] +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// CHECK-NORMAL-NOEXCEPT: unreachable: +// CHECK-NORMAL-NOEXCEPT-NEXT: unreachable +// +// CHECK-STRICT-NOEXCEPT: Function Attrs: mustprogress noinline optnone +// CHECK-STRICT-NOEXCEPT-LABEL: define {{[^@]+}}@_Z22exception_escape_is_oki +// CHECK-STRICT-NOEXCEPT-SAME: (i32 noundef [[X:%.*]]) #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 { +// CHECK-STRICT-NOEXCEPT-NEXT: entry: +// CHECK-STRICT-NOEXCEPT-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE:%.*]] = alloca [[STRUCT_S:%.*]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE3:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: [[STATE13:%.*]] = alloca [[STRUCT_S]], align 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]], i32 noundef 100) +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 200) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT:%.*]] unwind label [[LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END]] +// CHECK-STRICT-NOEXCEPT: lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: cleanup +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[EH_RESUME:%.*]] +// CHECK-STRICT-NOEXCEPT: if.end: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP4:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP4]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END10:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then2: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]], i32 noundef 300) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT5:%.*]] unwind label [[LPAD4:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont5: +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z11might_throwi(i32 noundef 400) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT7:%.*]] unwind label [[LPAD6:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont7: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[TRY_CONT:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad4: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP5:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP6]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP5]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP7]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad6: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP8:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP9:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP9]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP10:%.*]] = extractvalue { ptr, i32 } [[TMP8]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP10]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE3]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[CATCH]] +// CHECK-STRICT-NOEXCEPT: catch: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP11:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @__cxa_rethrow() #[[ATTR6]] +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[UNREACHABLE:%.*]] unwind label [[LPAD8:%.*]] +// CHECK-STRICT-NOEXCEPT: lpad8: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP12:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: cleanup +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP13]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP14:%.*]] = extractvalue { ptr, i32 } [[TMP12]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP14]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @__cxa_end_catch() +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT9:%.*]] unwind label [[TERMINATE_LPAD:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont9: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-STRICT-NOEXCEPT: try.cont: +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END10]] +// CHECK-STRICT-NOEXCEPT: if.end10: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP15:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[CMP11:%.*]] = icmp eq i32 [[TMP15]], 2 +// CHECK-STRICT-NOEXCEPT-NEXT: br i1 [[CMP11]], label [[IF_THEN12:%.*]], label [[IF_END16:%.*]] +// CHECK-STRICT-NOEXCEPT: if.then12: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SC1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]], i32 noundef 600) +// CHECK-STRICT-NOEXCEPT-NEXT: invoke void @_Z10will_throwi(i32 noundef 700) +// CHECK-STRICT-NOEXCEPT-NEXT: to label [[INVOKE_CONT15:%.*]] unwind label [[LPAD14:%.*]] +// CHECK-STRICT-NOEXCEPT: invoke.cont15: +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[IF_END16]] +// CHECK-STRICT-NOEXCEPT: lpad14: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP16:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: cleanup +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP17:%.*]] = extractvalue { ptr, i32 } [[TMP16]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: store ptr [[TMP17]], ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP18:%.*]] = extractvalue { ptr, i32 } [[TMP16]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: store i32 [[TMP18]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) [[STATE13]]) #[[ATTR5]] +// CHECK-STRICT-NOEXCEPT-NEXT: br label [[EH_RESUME]] +// CHECK-STRICT-NOEXCEPT: if.end16: +// CHECK-STRICT-NOEXCEPT-NEXT: ret void +// CHECK-STRICT-NOEXCEPT: eh.resume: +// CHECK-STRICT-NOEXCEPT-NEXT: [[EXN17:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-STRICT-NOEXCEPT-NEXT: [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-STRICT-NOEXCEPT-NEXT: [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr [[EXN17]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: [[LPAD_VAL18:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1 +// CHECK-STRICT-NOEXCEPT-NEXT: resume { ptr, i32 } [[LPAD_VAL18]] +// CHECK-STRICT-NOEXCEPT: terminate.lpad: +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP19:%.*]] = landingpad { ptr, i32 } +// CHECK-STRICT-NOEXCEPT-NEXT: catch ptr null +// CHECK-STRICT-NOEXCEPT-NEXT: [[TMP20:%.*]] = extractvalue { ptr, i32 } [[TMP19]], 0 +// CHECK-STRICT-NOEXCEPT-NEXT: call void @__clang_call_terminate(ptr [[TMP20]]) #[[ATTR7:[0-9]+]] +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// CHECK-STRICT-NOEXCEPT: unreachable: +// CHECK-STRICT-NOEXCEPT-NEXT: unreachable +// +void exception_escape_is_ok(int x) { + if(x == 0) { +#line 100 + S state; +#line 200 + might_throw(); + } + if(x == 1) { + try { +#line 300 + S state; +#line 400 + might_throw(); + } catch (...) { +#line 500 + throw; + } + } + if(x == 2) { +#line 600 + S state; +#line 700 + will_throw(); + } +} +//. +// CHECK-NO-EXCEPTIONS: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #2 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NO-EXCEPTIONS: attributes #3 = { nounwind } +//. +// CHECK-NORMAL-NOEXCEPT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #2 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #3 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #4 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-NORMAL-NOEXCEPT: attributes #5 = { nounwind } +// CHECK-NORMAL-NOEXCEPT: attributes #6 = { noreturn } +// CHECK-NORMAL-NOEXCEPT: attributes #7 = { noreturn nounwind } +//. +// CHECK-STRICT-NOEXCEPT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #2 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #3 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #4 = { noinline noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// CHECK-STRICT-NOEXCEPT: attributes #5 = { nounwind } +// CHECK-STRICT-NOEXCEPT: attributes #6 = { noreturn } +// CHECK-STRICT-NOEXCEPT: attributes #7 = { noreturn nounwind } +//. +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// CHECK-ALL: {{.*}} +// CHECK-EXCEPTIONS: {{.*}} Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -371,6 +371,10 @@ CmdArgs.push_back("-fcxx-exceptions"); EH = true; + + if (Args.hasFlag(options::OPT_fstrict_noexcept, + options::OPT_fno_strict_noexcept, false)) + CmdArgs.push_back("-fstrict-noexcept"); } } 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(bool isSanitized); + + /// 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; @@ -349,6 +355,12 @@ /// Determines whether the exception-scopes stack is empty. bool empty() const { return StartOfData == EndOfBuffer; } + /// An unstable reference to a scope-stack depth. Invalidated by + /// pushes but not pops. + class iterator; + + EHScopeStack::iterator getInnermostEHScopeForUnwind() const; + bool wouldUnwindBeUB() const; bool requiresLandingPad() const; /// Determines whether there are any normal cleanups on the stack. @@ -367,11 +379,6 @@ return InnermostEHScope; } - - /// An unstable reference to a scope-stack depth. Invalidated by - /// pushes but not pops. - class iterator; - /// Returns an iterator pointing to the innermost EH scope. iterator begin() const; Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -2382,6 +2382,9 @@ /// Emit a test that checks if the return value \p RV is nonnull. void EmitReturnValueCheck(llvm::Value *RV); + /// Internal to `EmitStartEHSpec()`/`EmitEndEHSpec()`, do not use directly. + bool ExceptionEscapeIsProgramTermination(const Decl *D, bool IsStart); + /// EmitStartEHSpec - Emit the start of the exception spec. void EmitStartEHSpec(const Decl *D); Index: clang/lib/CodeGen/CGException.cpp =================================================================== --- clang/lib/CodeGen/CGException.cpp +++ clang/lib/CodeGen/CGException.cpp @@ -459,80 +459,12 @@ EmitBlock(createBasicBlock("throw.cont")); } -void CodeGenFunction::EmitStartEHSpec(const Decl *D) { - if (!CGM.getLangOpts().CXXExceptions) - return; - - 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; - } - const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>(); - if (!Proto) - return; - - ExceptionSpecificationType EST = Proto->getExceptionSpecType(); - // In C++17 and later, 'throw()' aka EST_DynamicNone is treated the same way - // as noexcept. In earlier standards, it is handled in this block, along with - // 'throw(X...)'. - if (EST == EST_Dynamic || - (EST == EST_DynamicNone && !getLangOpts().CPlusPlus17)) { - // 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; - // 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(); - else - CGM.getDiags().Report(D->getLocation(), - diag::warn_wasm_dynamic_exception_spec_ignored) - << FD->getExceptionSpecSourceRange(); - return; - } - // 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 - // to do anything in that case. Just print a warning message in case of - // throw with types. - // TODO Correctly handle exception specification in Emscripten EH - if (getTarget().getCXXABI() == TargetCXXABI::WebAssembly && - CGM.getLangOpts().getExceptionHandling() == - LangOptions::ExceptionHandlingKind::None && - EST == EST_Dynamic) - CGM.getDiags().Report(D->getLocation(), - diag::warn_wasm_dynamic_exception_spec_ignored) - << FD->getExceptionSpecSourceRange(); - - unsigned NumExceptions = Proto->getNumExceptions(); - EHFilterScope *Filter = EHStack.pushFilter(NumExceptions); - - for (unsigned I = 0; I != NumExceptions; ++I) { - QualType Ty = Proto->getExceptionType(I); - QualType ExceptType = Ty.getNonReferenceType().getUnqualifiedType(); - llvm::Value *EHType = CGM.GetAddrOfRTTIDescriptor(ExceptType, - /*ForEH=*/true); - Filter->setFilter(I, EHType); - } - } else if (Proto->canThrow() == CT_Cannot) { - // noexcept functions are simple terminate scopes. - if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur - EHStack.pushTerminate(); - } -} - /// Emit the dispatch block for a filter scope if necessary. static void emitFilterDispatchBlock(CodeGenFunction &CGF, EHFilterScope &filterScope) { llvm::BasicBlock *dispatchBlock = filterScope.getCachedEHDispatchBlock(); - if (!dispatchBlock) return; + if (!dispatchBlock) + return; if (dispatchBlock->use_empty()) { delete dispatchBlock; return; @@ -561,51 +493,133 @@ // according to the last landing pad the exception was thrown // into. Seriously. llvm::Value *exn = CGF.getExceptionFromSlot(); - CGF.EmitRuntimeCall(getUnexpectedFn(CGF.CGM), exn) - ->setDoesNotReturn(); + CGF.EmitRuntimeCall(getUnexpectedFn(CGF.CGM), exn)->setDoesNotReturn(); CGF.Builder.CreateUnreachable(); } +bool CodeGenFunction::ExceptionEscapeIsProgramTermination(const Decl *D, + bool IsStart) { + 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()) + return true; + } + return false; + } + + const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>(); + if (!Proto) + return false; + + ExceptionSpecificationType EST = Proto->getExceptionSpecType(); + // In C++17 and later, 'throw()' aka EST_DynamicNone is treated the same way + // as noexcept. In earlier standards, it is handled in this block, along with + // 'throw(X...)'. + if (EST == EST_Dynamic || + (EST == EST_DynamicNone && !getLangOpts().CPlusPlus17)) { + // 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 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) + return true; + else if (IsStart) + CGM.getDiags().Report(D->getLocation(), + diag::warn_wasm_dynamic_exception_spec_ignored) + << FD->getExceptionSpecSourceRange(); + 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 + // to do anything in that case. Just print a warning message in case of + // throw with types. + // TODO Correctly handle exception specification in Emscripten EH + if (getTarget().getCXXABI() == TargetCXXABI::WebAssembly && + CGM.getLangOpts().getExceptionHandling() == + LangOptions::ExceptionHandlingKind::None && + EST == EST_Dynamic && IsStart) + CGM.getDiags().Report(D->getLocation(), + diag::warn_wasm_dynamic_exception_spec_ignored) + << FD->getExceptionSpecSourceRange(); + + if (IsStart) { + unsigned NumExceptions = Proto->getNumExceptions(); + EHFilterScope *Filter = EHStack.pushFilter(NumExceptions); + + for (unsigned I = 0; I != NumExceptions; ++I) { + QualType Ty = Proto->getExceptionType(I); + QualType ExceptType = Ty.getNonReferenceType().getUnqualifiedType(); + llvm::Value *EHType = CGM.GetAddrOfRTTIDescriptor(ExceptType, + /*ForEH=*/true); + Filter->setFilter(I, EHType); + } + } else { + EHFilterScope &filterScope = cast<EHFilterScope>(*EHStack.begin()); + emitFilterDispatchBlock(*this, filterScope); + EHStack.popFilter(); + } + } else if (Proto->canThrow() == CT_Cannot) { + // noexcept functions are simple terminate scopes. + if (!getLangOpts().EHAsynch) // -EHa: HW exception still can occur + return true; + } + return false; +} + +void CodeGenFunction::EmitStartEHSpec(const Decl *D) { + if (!CGM.getLangOpts().CXXExceptions) + return; + + // If the LLVM IR function is marked as non-unwinding, + // then exception escape is UB. UBSan might be interested in this. + // FIXME: why is -EHa special? This seems broken. + if (CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) && + !getLangOpts().EHAsynch) + EHStack.pushUB(/*isSanitized=*/false); // FIXME: implement UBSan bits. + + // 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 (ExceptionEscapeIsProgramTermination(D, /*IsStart=*/true)) { + assert(CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) && + "Forgot to manifest nounwind function attribute in LLVM IR."); + // Are we *NOT* in the language dialect where this situation is allowed to + // cause Undefined Behaviour instead of guaranteed program termination? + if (!CGM.getCodeGenOpts().StrictNoexcept) + EHStack.pushTerminate(); + } +} + void CodeGenFunction::EmitEndEHSpec(const Decl *D) { if (!CGM.getLangOpts().CXXExceptions) return; - 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; + // 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 (ExceptionEscapeIsProgramTermination(D, /*IsStart=*/false)) { + assert(CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) && + "Forgot to manifest nounwind function attribute in LLVM IR."); + // Are we *NOT* in the language dialect where this situation is allowed to + // cause Undefined Behaviour instead of guaranteed program termination? + if (!CGM.getCodeGenOpts().StrictNoexcept) + EHStack.popTerminate(); } - const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>(); - if (!Proto) - return; - ExceptionSpecificationType EST = Proto->getExceptionSpecType(); - if (EST == EST_Dynamic || - (EST == EST_DynamicNone && !getLangOpts().CPlusPlus17)) { - // 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; - // 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; - } - EHFilterScope &filterScope = cast<EHFilterScope>(*EHStack.begin()); - emitFilterDispatchBlock(*this, filterScope); - EHStack.popFilter(); - } else if (Proto->canThrow() == CT_Cannot && - /* possible empty when under async exceptions */ - !EHStack.empty()) { - EHStack.popTerminate(); - } + // If the LLVM IR function is marked as non-unwinding, + // then exception escape is UB. UBSan might be interested in this. + // FIXME: why is -EHa special? This seems broken. + if (CurFn->hasFnAttribute(llvm::Attribute::NoUnwind) && + !getLangOpts().EHAsynch) + EHStack.popUB(); } void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) { @@ -692,6 +706,11 @@ case EHScope::Terminate: dispatchBlock = getTerminateHandler(); break; + + case EHScope::UB: + assert(!cast<EHUBScope>(scope).getIsSanitized()); + dispatchBlock = getUnreachableBlock(); + break; } scope.setCachedEHDispatchBlock(dispatchBlock); } @@ -733,6 +752,10 @@ case EHScope::Terminate: DispatchBlock->setName("terminate"); break; + + case EHScope::UB: + llvm_unreachable("Will never be called."); + break; } EHS.setCachedEHDispatchBlock(DispatchBlock); return DispatchBlock; @@ -748,6 +771,7 @@ case EHScope::Filter: case EHScope::Catch: case EHScope::Terminate: + case EHScope::UB: return false; } @@ -813,6 +837,9 @@ switch (innermostEHScope.getKind()) { case EHScope::Terminate: return getTerminateLandingPad(); + case EHScope::UB: + llvm_unreachable("Will never be called."); + break; case EHScope::Catch: case EHScope::Cleanup: @@ -858,7 +885,8 @@ continue; case EHScope::Filter: { - assert(I.next() == EHStack.end() && "EH filter is not end of EH stack"); + assert((I.next() == EHStack.end() || isa<EHUBScope>(*I.next())) && + "EH filter is not end of EH stack"); assert(!hasCatchAll && "EH filter reached after catch-all"); // Filter scopes get added to the landingpad in weird ways. @@ -872,7 +900,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 +972,7 @@ Builder.restoreIP(savedIP); return lpad; -} + } static void emitCatchPadBlock(CodeGenFunction &CGF, EHCatchScope &CatchScope) { llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock(); Index: clang/lib/CodeGen/CGCleanup.h =================================================================== --- clang/lib/CodeGen/CGCleanup.h +++ clang/lib/CodeGen/CGCleanup.h @@ -86,6 +86,13 @@ unsigned CleanupSize : 12; }; + class UBBitFields { + friend class EHUBScope; + unsigned : NumCommonBits; + + unsigned IsSanitized : 32 - NumCommonBits; + }; + class FilterBitFields { friend class EHFilterScope; unsigned : NumCommonBits; @@ -97,16 +104,18 @@ CommonBitFields CommonBits; CatchBitFields CatchBits; CleanupBitFields CleanupBits; + UBBitFields UBBits; FilterBitFields FilterBits; }; 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), EnclosingEHScope(enclosingEHScope) { CommonBits.Kind = kind; + assert(CommonBits.Kind == kind && "Kind overflow?"); } Kind getKind() const { return static_cast<Kind>(CommonBits.Kind); } @@ -487,6 +496,22 @@ } }; +/// An exceptions scope which causes UB if any exception reaches it. +class EHUBScope : public EHScope { +public: + EHUBScope(bool isSanitized, EHScopeStack::stable_iterator enclosingEHScope) + : EHScope(UB, enclosingEHScope) { + UBBits.IsSanitized = isSanitized; + assert(UBBits.IsSanitized == isSanitized && "IsSanitized overflow?"); + } + + bool getIsSanitized() const { return UBBits.IsSanitized; } + + 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 +549,10 @@ case EHScope::Terminate: Size = EHTerminateScope::getSize(); break; + + case EHScope::UB: + Size = EHUBScope::getSize(); + break; } Ptr += llvm::alignTo(Size, ScopeStackAlignment); return *this; @@ -572,6 +601,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 @@ -152,7 +152,7 @@ return true; } -bool EHScopeStack::requiresLandingPad() const { +EHScopeStack::iterator EHScopeStack::getInnermostEHScopeForUnwind() const { for (stable_iterator si = getInnermostEHScope(); si != stable_end(); ) { // Skip lifetime markers. if (auto *cleanup = dyn_cast<EHCleanupScope>(&*find(si))) @@ -160,10 +160,30 @@ si = cleanup->getEnclosingEHScope(); continue; } - return true; + return find(si); } - return false; + return end(); +} + +bool EHScopeStack::wouldUnwindBeUB() const { + EHScopeStack::iterator it = getInnermostEHScopeForUnwind(); + if (it == end()) + return false; + + // If we are in exception scope which causes UB if any exception reaches it, + // and we are not sanitizing for that UB, then the unwind would indeed be UB. + // because the callee will not unwind and the landing pad won't be reached. + auto *ub = dyn_cast<EHUBScope>(&*it); + return ub && !ub->getIsSanitized(); +} + +bool EHScopeStack::requiresLandingPad() const { + EHScopeStack::iterator it = getInnermostEHScopeForUnwind(); + if (it == end()) + return false; + + return !wouldUnwindBeUB(); } EHScopeStack::stable_iterator @@ -188,9 +208,15 @@ // some, or all cleanups are called before std::terminate. Thus, when // terminate is the current EH scope, we may skip adding any EH cleanup // scopes. - if (InnermostEHScope != stable_end() && - find(InnermostEHScope)->getKind() == EHScope::Terminate) - IsEHCleanup = false; + // + // Likewise, if the exception escaping out of the function would be UB, + // skip adding any EH cleanup scopes. + if (InnermostEHScope != stable_end()) { + if (EHScope::Kind InnermostEHScopeKind = find(InnermostEHScope)->getKind(); + InnermostEHScopeKind == EHScope::Terminate || + InnermostEHScopeKind == EHScope::UB) + IsEHCleanup = false; + } EHCleanupScope *Scope = new (Buffer) EHCleanupScope(IsNormalCleanup, @@ -240,7 +266,8 @@ } EHFilterScope *EHScopeStack::pushFilter(unsigned numFilters) { - assert(getInnermostEHScope() == stable_end()); + assert(getInnermostEHScope() == stable_end() || + isa<EHUBScope>(*find(getInnermostEHScope()))); char *buffer = allocate(EHFilterScope::getSizeForNumFilters(numFilters)); EHFilterScope *filter = new (buffer) EHFilterScope(numFilters); InnermostEHScope = stable_begin(); @@ -270,6 +297,12 @@ InnermostEHScope = stable_begin(); } +void EHScopeStack::pushUB(bool isSanitized) { + char *Buffer = allocate(EHUBScope::getSize()); + new (Buffer) EHUBScope(isSanitized, 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 @@ -5411,6 +5411,9 @@ llvm::CallBase *CI; if (!InvokeDest) { CI = Builder.CreateCall(IRFuncTy, CalleePtr, IRCallArgs, BundleList); + if (EHStack.wouldUnwindBeUB()) + Attrs = Attrs.addFnAttribute(getLLVMContext(), + llvm::Attribute::AttrKind::NoUnwind); } else { llvm::BasicBlock *Cont = createBasicBlock("invoke.cont"); CI = Builder.CreateInvoke(IRFuncTy, CalleePtr, Cont, InvokeDest, IRCallArgs, Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1576,6 +1576,14 @@ def fignore_exceptions : Flag<["-"], "fignore-exceptions">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Enable support for ignoring exception handling constructs">, MarshallingInfoFlag<LangOpts<"IgnoreExceptions">>; +defm strict_noexcept : BoolFOption<"strict-noexcept", + CodeGenOpts<"StrictNoexcept">, DefaultFalse, + PosFlag<SetTrue, [CC1Option], "Exception escape out of a function with noexcept " + "exception specification is Undefined Behaviour.">, + NegFlag<SetFalse, [], "Exception escape out of a function with noexcept exception " + "specification causes program termination, " + "as mandated by the C++ language rules. (default)">, + BothFlags<[CoreOption]>>; def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>, HelpText<"Allows control over excess precision on targets where native " "support for the precision types is not available. By default, excess " Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -504,6 +504,10 @@ /// non-deleting destructors. (No effect on Microsoft ABI.) CODEGENOPT(CtorDtorReturnThis, 1, 0) +/// When enabled, redefines exception escape out of an `noexcept` function +/// from causing program termination to causing UB (default off). +CODEGENOPT(StrictNoexcept, 1, 0) + #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT Index: clang/docs/UsersManual.rst =================================================================== --- clang/docs/UsersManual.rst +++ clang/docs/UsersManual.rst @@ -1787,6 +1787,12 @@ * ``16`` - Forces ``_Float16`` operations to be emitted without using excess precision arithmetic. +.. option:: `-fstrict-noexcept` + By default, if an exception tries to escape out of a function with C++'s + ``noexcept`` exception specification, the program will be terminated. + This option (default off), allows to opt-in into a language dialect, + where such an exception escape causes Undefined Behaviour instead. + .. _crtfastmath.o: A note about ``crtfastmath.o`` Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -532,6 +532,14 @@ int b[0]; // NOT a flexible array member. }; +- :option:`-fstrict-noexcept` and + :option:`-fno-strict-noexcept` - + By default, if an exception tries to escape out of a function with C++'s + ``noexcept`` exception specification, the program will be terminated. + This option (default off), allows to opt-in into a language dialect, + where such an exception escape causes Undefined Behaviour instead. + + Deprecated Compiler Flags ------------------------- - ``-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang``
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits