Introduce a dedicated cleanup flag for SEH __finally blocks and use it to separate SEH try cleanup emission from C++ object cleanup emission This prevents __finally cleanups from emitting seh.scope.begin/end and keeps destructor/delete cleanups paired with seh.scope markers Fix #109576
204 lines
5.2 KiB
C++
204 lines
5.2 KiB
C++
// RUN: %clang_cc1 -triple x86_64-windows -fasync-exceptions -fcxx-exceptions -fexceptions -fms-extensions -x c++ -Wno-implicit-function-declaration -emit-llvm %s -o - | FileCheck %s
|
|
|
|
// CHECK-LABEL: @main()
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK: %[[src:[0-9-]+]] = load volatile i32, ptr %i
|
|
// CHECK-NEXT: i32 noundef %[[src]]
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
|
|
// CHECK: define internal void @"?fin$0@0@main@@"(i8 noundef %abnormal_termination
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
|
|
// *****************************************************************************
|
|
// Abstract: Test __Try in __finally under SEH -EHa option
|
|
void printf(...);
|
|
int volatile *NullPtr = 0;
|
|
int main() {
|
|
for (int i = 0; i < 3; i++) {
|
|
printf(" --- Test _Try in _finally --- i = %d \n", i);
|
|
__try {
|
|
__try {
|
|
printf(" In outer _try i = %d \n", i);
|
|
if (i == 0)
|
|
*NullPtr = 0;
|
|
} __finally {
|
|
__try {
|
|
printf(" In outer _finally i = %d \n", i);
|
|
if (i == 1)
|
|
*NullPtr = 0;
|
|
} __finally {
|
|
printf(" In Inner _finally i = %d \n", i);
|
|
if (i == 2)
|
|
*NullPtr = 0;
|
|
}
|
|
}
|
|
} __except (1) {
|
|
printf(" --- In outer except handler i = %d \n", i);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// CHECK-LABEL:@"?foo@@YAXXZ"()
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK: invoke.cont:
|
|
// CHECK: store volatile i32 1, ptr %cleanup.dest.slot
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
// CHECK: invoke.cont1:
|
|
// CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot
|
|
// CHECK: %1 = icmp ne i32 %cleanup.dest, 0
|
|
// CHECK: %2 = zext i1 %1 to i8
|
|
// CHECK: call void @"?fin$0@0@foo@@"(i8 noundef %2, ptr noundef %0)
|
|
// CHECK: ehcleanup:
|
|
// CHECK: call void @"?fin$0@0@foo@@"(i8 noundef 1, ptr noundef %4)
|
|
void foo()
|
|
{
|
|
__try {
|
|
return;
|
|
}
|
|
__finally {
|
|
if (_abnormal_termination()) {
|
|
printf("Passed\n");
|
|
} else {
|
|
printf("Failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL:@"?bar@@YAHXZ"()
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK: invoke.cont:
|
|
// CHECK: store volatile i32 1, ptr %cleanup.dest.slot
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
// CHECK: invoke.cont1:
|
|
// CHECK: call void @"?fin$0@0@bar@@"
|
|
// CHECK: %cleanup.dest2 = load i32, ptr %cleanup.dest.slot
|
|
// CHECK: return:
|
|
// CHECK: ret i32 11
|
|
int bar()
|
|
{
|
|
int x;
|
|
__try {
|
|
return 11;
|
|
} __finally {
|
|
if (_abnormal_termination()) {
|
|
x = 9;
|
|
}
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: @"?foo1@@YAXXZ"()
|
|
//
|
|
// CHECK-NOT: invoke void @llvm.seh.scope.begin()
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
//
|
|
// CHECK-NOT: invoke void @llvm.seh.try.begin()
|
|
// CHECK: invoke.cont1:
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.begin()
|
|
// CHECK: invoke noundef ptr @"??0A@@QEAA@XZ"
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
//
|
|
// CHECK-NOT: @llvm.seh
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.begin()
|
|
//
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: invoke void @"??1A@@QEAA@XZ"
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
// CHECK-NOT: @llvm.seh
|
|
// CHECK: br label %delete.end
|
|
//
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
//
|
|
// CHECK-NOT: invoke void @llvm.seh.try.end()
|
|
// CHECK: invoke.cont15:
|
|
//
|
|
// CHECK: call void @"?fin$0@0@foo1@@"(i8 noundef 0
|
|
// CHECK-NOT: @llvm.seh
|
|
//
|
|
// CHECK: ehcleanup:
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
// CHECK: invoke.cont7:
|
|
// CHECK: cleanupret
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
// CHECK: invoke.cont14:
|
|
// CHECK: cleanupret
|
|
//
|
|
// CHECK: ehcleanup16:
|
|
// CHECK: call void @"?fin$0@0@foo1@@"(i8 noundef 1
|
|
// CHECK-NOT: @llvm.seh
|
|
// CHECK: cleanupret
|
|
struct A {
|
|
A() noexcept;
|
|
~A();
|
|
};
|
|
|
|
void foo1() {
|
|
__try {
|
|
A* a = new A;
|
|
delete a;
|
|
}
|
|
__finally {
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: @"?seh_finally_no_obj@@YAXXZ"()
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: invoke void @"?might_throw@@YAXXZ"()
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: call void @"?fin$0@0@seh_finally_no_obj@@"(i8 noundef 0
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: call void @"?fin$0@0@seh_finally_no_obj@@"(i8 noundef 1
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: cleanupret
|
|
void might_throw();
|
|
void seh_finally_no_obj() {
|
|
__try {
|
|
might_throw();
|
|
}
|
|
__finally {
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: @"?seh_finally_with_obj@@YAXPEAUA@@@Z"(
|
|
//
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: invoke void @llvm.seh.try.begin()
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.begin()
|
|
// CHECK: invoke void @"??1A@@QEAA@XZ"
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
//
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
//
|
|
// CHECK: invoke void @llvm.seh.try.end()
|
|
//
|
|
// CHECK-NOT: @llvm.seh.scope
|
|
// CHECK: call void @"?fin$0@0@seh_finally_with_obj@@"(i8 noundef 0
|
|
//
|
|
// CHECK: invoke void @llvm.seh.scope.end()
|
|
// CHECK: cleanupret
|
|
//
|
|
// CHECK: call void @"?fin$0@0@seh_finally_with_obj@@"(i8 noundef 1
|
|
// CHECK-NOT: @llvm.seh
|
|
// CHECK: cleanupret
|
|
void seh_finally_with_obj(A* p) {
|
|
__try {
|
|
delete p;
|
|
}
|
|
__finally {
|
|
}
|
|
}
|