// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
// RUN: %clang_cc1 %s -triple i686-pc-win32 -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
// RUN: %clang_cc1 %s -triple aarch64-windows -fms-extensions -emit-llvm -O1 -disable-llvm-passes -o - | FileCheck %s
// NOTE: we're passing "-O1 -disable-llvm-passes" to avoid adding optnone and noinline everywhere.
void abort(void) __attribute__((noreturn));
void might_crash(void);
void cleanup(void);
int check_condition(void);
void basic_finally(void) {
__try {
might_crash();
} __finally {
cleanup();
}
}
// CHECK-LABEL: define dso_local void @basic_finally()
// CHECK: invoke void @might_crash()
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[invoke_cont]]
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 0, ptr noundef %[[fp]])
// CHECK-NEXT: ret void
//
// CHECK: [[lpad]]
// CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK: call void @"?fin$0@0@basic_finally@@"({{i8 noundef( zeroext)?}} 1, ptr noundef %[[fp]])
// CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
// CHECK: define internal void @"?fin$0@0@basic_finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs:#[0-9]+]]
// CHECK: call void @cleanup()
// Mostly check that we don't double emit 'r' which would crash.
void decl_in_finally(void) {
__try {
might_crash();
} __finally {
int r;
}
}
// Ditto, don't crash double emitting 'l'.
void label_in_finally(void) {
__try {
might_crash();
} __finally {
l:
cleanup();
if (check_condition())
goto l;
}
}
// CHECK-LABEL: define dso_local void @label_in_finally()
// CHECK: invoke void @might_crash()
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[invoke_cont]]
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK: call void @"?fin$0@0@label_in_finally@@"({{i8 noundef( zeroext)?}} 0, ptr noundef %[[fp]])
// CHECK: ret void
// CHECK: define internal void @"?fin$0@0@label_in_finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: br label %[[l:[^ ]*]]
//
// CHECK: [[l]]
// CHECK: call void @cleanup()
// CHECK: call i32 @check_condition()
// CHECK: br i1 {{.*}}, label
// CHECK: br label %[[l]]
int crashed;
void use_abnormal_termination(void) {
__try {
might_crash();
} __finally {
crashed = __abnormal_termination();
}
}
// CHECK-LABEL: define dso_local void @use_abnormal_termination()
// CHECK: invoke void @might_crash()
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[invoke_cont]]
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( zeroext)?}} 0, ptr noundef %[[fp]])
// CHECK: ret void
//
// CHECK: [[lpad]]
// CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
// CHECK: %[[fp:[^ ]*]] = call ptr @llvm.localaddress()
// CHECK: call void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( zeroext)?}} 1, ptr noundef %[[fp]])
// CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
// CHECK: define internal void @"?fin$0@0@use_abnormal_termination@@"({{i8 noundef( zeroext)?}} %[[abnormal:abnormal_termination]], ptr noundef %frame_pointer)
// CHECK-SAME: [[finally_attrs]]
// CHECK: %[[abnormal_zext:[^ ]*]] = zext i8 %[[abnormal]] to i32
// CHECK: store i32 %[[abnormal_zext]], ptr @crashed
// CHECK-NEXT: ret void
void noreturn_noop_finally(void) {
__try {
__noop();
} __finally {
abort();
}
}
// CHECK-LABEL: define dso_local void @noreturn_noop_finally()
// CHECK: call void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
// CHECK: ret void
// CHECK: define internal void @"?fin$0@0@noreturn_noop_finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: call void @abort()
// CHECK: unreachable
void noreturn_finally(void) {
__try {
might_crash();
} __finally {
abort();
}
}
// CHECK-LABEL: define dso_local void @noreturn_finally()
// CHECK: invoke void @might_crash()
// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[cont]]
// CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
// CHECK: ret void
//
// CHECK: [[lpad]]
// CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
// CHECK: call void @"?fin$0@0@noreturn_finally@@"({{.*}})
// CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
// CHECK: define internal void @"?fin$0@0@noreturn_finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: call void @abort()
// CHECK: unreachable
int finally_with_return(void) {
__try {
return 42;
} __finally {
}
}
// CHECK-LABEL: define dso_local i32 @finally_with_return()
// CHECK: store i32 1, ptr %cleanup.dest.slot
// CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot
// CHECK: icmp ne i32 %cleanup.dest
// CHECK: call void @"?fin$0@0@finally_with_return@@"({{.*}})
// CHECK: ret i32 42
// CHECK: define internal void @"?fin$0@0@finally_with_return@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK-NOT: br i1
// CHECK-NOT: br label
// CHECK: ret void
int nested___finally___finally(void) {
__try {
__try {
} __finally {
return 1;
}
} __finally {
// Intentionally no return here.
}
return 0;
}
// CHECK-LABEL: define dso_local i32 @nested___finally___finally
// CHECK: invoke void @"?fin$1@0@nested___finally___finally@@"({{.*}})
// CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
//
// CHECK: [[outercont]]
// CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
// CHECK-NEXT: ret i32 0
//
// CHECK: [[lpad]]
// CHECK-NEXT: %[[pad:[^ ]*]] = cleanuppad
// CHECK: call void @"?fin$0@0@nested___finally___finally@@"({{.*}})
// CHECK-NEXT: cleanupret from %[[pad]] unwind to caller
// CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: ret void
// CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: unreachable
// FIXME: Our behavior seems suspiciously different.
int nested___finally___finally_with_eh_edge(void) {
__try {
__try {
might_crash();
} __finally {
return 899;
}
} __finally {
// Intentionally no return here.
}
return 912;
}
// CHECK-LABEL: define dso_local i32 @nested___finally___finally_with_eh_edge
// CHECK: invoke void @might_crash()
// CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad1:[^ ]*]]
//
// [[invokecont]]
// CHECK: invoke void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-NEXT: to label %[[outercont:[^ ]*]] unwind label %[[lpad2:[^ ]*]]
//
// CHECK: [[outercont]]
// CHECK: call void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-NEXT: ret i32 912
//
// CHECK: [[lpad1]]
// CHECK-NEXT: %[[innerpad:[^ ]*]] = cleanuppad
// CHECK: invoke void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-NEXT: label %[[innercleanupretbb:[^ ]*]] unwind label %[[lpad2:[^ ]*]]
//
// CHECK: [[innercleanupretbb]]
// CHECK-NEXT: cleanupret from %[[innerpad]] unwind label %[[lpad2]]
//
// CHECK: [[lpad2]]
// CHECK-NEXT: %[[outerpad:[^ ]*]] = cleanuppad
// CHECK: call void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-NEXT: cleanupret from %[[outerpad]] unwind to caller
// CHECK-LABEL: define internal void @"?fin$0@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: ret void
// CHECK-LABEL: define internal void @"?fin$1@0@nested___finally___finally_with_eh_edge@@"({{.*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: unreachable
void finally_within_finally(void) {
__try {
might_crash();
} __finally {
__try {
might_crash();
} __finally {
}
}
}
// CHECK-LABEL: define dso_local void @finally_within_finally(
// CHECK: invoke void @might_crash(
// CHECK: call void @"?fin$0@0@finally_within_finally@@"(
// CHECK: call void @"?fin$0@0@finally_within_finally@@"({{.*}}) [ "funclet"(
// CHECK-LABEL: define internal void @"?fin$0@0@finally_within_finally@@"({{[^)]*}})
// CHECK-SAME: [[finally_attrs]]
// CHECK: invoke void @might_crash(
// CHECK: call void @"?fin$1@0@finally_within_finally@@"(
// CHECK: call void @"?fin$1@0@finally_within_finally@@"({{.*}}) [ "funclet"(
// CHECK-LABEL: define internal void @"?fin$1@0@finally_within_finally@@"({{[^)]*}})
// CHECK-SAME: [[finally_attrs]]
void cleanup_with_func(const char *);
void finally_with_func(void) {
__try {
might_crash();
} __finally {
cleanup_with_func(__func__);
}
}
// CHECK-LABEL: define internal void @"?fin$0@0@finally_with_func@@"({{[^)]*}})
// CHECK: call void @cleanup_with_func(ptr noundef @"??_C@_0BC@COAGBPGM@finally_with_func?$AA@")
// Look for the absence of noinline. nounwind is expected; any further
// attributes should be string attributes.
// CHECK: attributes [[finally_attrs]] = { nounwind "{{.*}}" }