| // RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c23 -fdefer-ts -emit-llvm %s -o - | FileCheck %s | |
| #define defer _Defer | |
| void a(); | |
| void b(); | |
| void c(); | |
| void x(int q); | |
| bool q(int q); | |
| [[noreturn]] void noreturn(); | |
| // CHECK-LABEL: define {{.*}} void @f1() | |
| void f1() { | |
| // CHECK: call void @c() | |
| // CHECK: call void @b() | |
| // CHECK: call void @a() | |
| defer a(); | |
| defer b(); | |
| defer c(); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @f2() | |
| void f2() { | |
| // CHECK: call void @x(i32 {{.*}} 1) | |
| // CHECK: call void @x(i32 {{.*}} 2) | |
| // CHECK: call void @x(i32 {{.*}} 3) | |
| // CHECK: call void @x(i32 {{.*}} 4) | |
| // CHECK: call void @x(i32 {{.*}} 5) | |
| defer x(5); | |
| { | |
| defer x(4); | |
| { | |
| defer x(2); | |
| defer x(1); | |
| } | |
| x(3); | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @f3(i1 {{.*}} %ret) | |
| void f3(bool ret) { | |
| // CHECK: entry: | |
| // CHECK: %ret.addr = alloca i8, align 1 | |
| // CHECK: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK: %storedv = zext i1 %ret to i8 | |
| // CHECK: store i8 %storedv, ptr %ret.addr, align 1 | |
| // CHECK: %0 = load i8, ptr %ret.addr, align 1 | |
| // CHECK: %loadedv = trunc i8 %0 to i1 | |
| // CHECK: br i1 %loadedv, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK: store i32 1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: br label %cleanup | |
| // CHECK: if.end: | |
| // CHECK: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: call void @x(i32 {{.*}} 1) | |
| // CHECK: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: br label %cleanup | |
| // CHECK: cleanup: | |
| // CHECK: %cleanup.dest.saved1 = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: call void @x(i32 {{.*}} 2) | |
| // CHECK: store i32 %cleanup.dest.saved1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: switch i32 %cleanup.dest, label %unreachable [ | |
| // CHECK: i32 0, label %cleanup.cont | |
| // CHECK: i32 1, label %cleanup.cont | |
| // CHECK: ] | |
| // CHECK: cleanup.cont: | |
| // CHECK: ret void | |
| // CHECK: unreachable: | |
| // CHECK: unreachable | |
| defer x(2); | |
| if (ret) return; | |
| defer x(1); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_g() | |
| void ts_g() { | |
| // CHECK-NEXT: entry: | |
| // CHECK-NEXT: ret void | |
| // CHECK-NEXT: } | |
| return; | |
| defer x(42); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_h() | |
| void ts_h() { | |
| // CHECK-NEXT: entry: | |
| // CHECK-NEXT: br label %b | |
| // CHECK-EMPTY: | |
| goto b; | |
| { | |
| defer x(42); | |
| } | |
| // CHECK-NEXT: b: | |
| // CHECK-NEXT: ret void | |
| // CHECK-NEXT: } | |
| b: | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_i() | |
| void ts_i() { | |
| // CHECK: entry: | |
| // CHECK: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK: store i32 2, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: call void @x(i32 {{.*}} 42) | |
| // CHECK: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: switch i32 %cleanup.dest, label %unreachable [ | |
| // CHECK: i32 2, label %b | |
| // CHECK: ] | |
| // CHECK: b: | |
| // CHECK: ret void | |
| // CHECK: unreachable: | |
| // CHECK: unreachable | |
| { | |
| defer { x(42); } | |
| goto b; | |
| } | |
| b: | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_m() | |
| void ts_m() { | |
| // CHECK: entry: | |
| // CHECK: br label %b | |
| // CHECK: b: | |
| // CHECK: call void @x(i32 {{.*}} 1) | |
| // CHECK: ret void | |
| goto b; | |
| { | |
| b: | |
| defer x(1); | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_p() | |
| void ts_p() { | |
| // CHECK: entry: | |
| // CHECK: br label %b | |
| // CHECK: b: | |
| // CHECK: ret void | |
| { | |
| goto b; | |
| defer x(42); | |
| } | |
| b: | |
| } | |
| // CHECK-LABEL: define {{.*}} void @ts_r() | |
| void ts_r() { | |
| // CHECK: entry: | |
| // CHECK: br label %b | |
| // CHECK: b: | |
| // CHECK: call void @x(i32 {{.*}} 42) | |
| // CHECK: br label %b | |
| { | |
| b: | |
| defer x(42); | |
| } | |
| goto b; | |
| } | |
| // CHECK-LABEL: define {{.*}} i32 @return_value() | |
| int return_value() { | |
| // CHECK: entry: | |
| // CHECK: %r = alloca i32, align 4 | |
| // CHECK: %p = alloca ptr, align 8 | |
| // CHECK: store i32 4, ptr %r, align 4 | |
| // CHECK: store ptr %r, ptr %p, align 8 | |
| // CHECK: %0 = load ptr, ptr %p, align 8 | |
| // CHECK: %1 = load i32, ptr %0, align 4 | |
| // CHECK: %2 = load ptr, ptr %p, align 8 | |
| // CHECK: store i32 5, ptr %2, align 4 | |
| // CHECK: ret i32 %1 | |
| int r = 4; | |
| int* p = &r; | |
| defer { *p = 5; } | |
| return *p; | |
| } | |
| void* malloc(__SIZE_TYPE__ size); | |
| void free(void* ptr); | |
| int use_buffer(__SIZE_TYPE__ size, void* ptr); | |
| // CHECK-LABEL: define {{.*}} i32 @malloc_free_example() | |
| int malloc_free_example() { | |
| // CHECK: entry: | |
| // CHECK: %size = alloca i32, align 4 | |
| // CHECK: %buf = alloca ptr, align 8 | |
| // CHECK: store i32 20, ptr %size, align 4 | |
| // CHECK: %call = call ptr @malloc(i64 {{.*}} 20) | |
| // CHECK: store ptr %call, ptr %buf, align 8 | |
| // CHECK: %0 = load ptr, ptr %buf, align 8 | |
| // CHECK: %call1 = call i32 @use_buffer(i64 {{.*}} 20, ptr {{.*}} %0) | |
| // CHECK: %1 = load ptr, ptr %buf, align 8 | |
| // CHECK: call void @free(ptr {{.*}} %1) | |
| // CHECK: ret i32 %call1 | |
| const int size = 20; | |
| void* buf = malloc(size); | |
| defer { free(buf); } | |
| return use_buffer(size, buf); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @sequencing_1() | |
| void sequencing_1() { | |
| // CHECK: entry: | |
| // CHECK: call void @x(i32 {{.*}} 1) | |
| // CHECK: call void @x(i32 {{.*}} 2) | |
| // CHECK: call void @x(i32 {{.*}} 3) | |
| // CHECK: ret void | |
| { | |
| defer { | |
| x(3); | |
| } | |
| if (true) | |
| defer x(1); | |
| x(2); | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @sequencing_2() | |
| void sequencing_2() { | |
| // CHECK: entry: | |
| // CHECK: %arr = alloca [3 x i32], align 4 | |
| // CHECK: %i = alloca i32, align 4 | |
| // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %arr, ptr align 4 @__const.sequencing_2.arr, i64 12, i1 false) | |
| // CHECK: store i32 0, ptr %i, align 4 | |
| // CHECK: br label %for.cond | |
| // CHECK: for.cond: | |
| // CHECK: %0 = load i32, ptr %i, align 4 | |
| // CHECK: %cmp = icmp ult i32 %0, 3 | |
| // CHECK: br i1 %cmp, label %for.body, label %for.end | |
| // CHECK: for.body: | |
| // CHECK: %1 = load i32, ptr %i, align 4 | |
| // CHECK: %idxprom = zext i32 %1 to i64 | |
| // CHECK: %arrayidx = getelementptr inbounds nuw [3 x i32], ptr %arr, i64 0, i64 %idxprom | |
| // CHECK: %2 = load i32, ptr %arrayidx, align 4 | |
| // CHECK: call void @x(i32 {{.*}} %2) | |
| // CHECK: br label %for.inc | |
| // CHECK: for.inc: | |
| // CHECK: %3 = load i32, ptr %i, align 4 | |
| // CHECK: %inc = add i32 %3, 1 | |
| // CHECK: store i32 %inc, ptr %i, align 4 | |
| // CHECK: br label %for.cond | |
| // CHECK: for.end: | |
| // CHECK: call void @x(i32 {{.*}} 4) | |
| // CHECK: call void @x(i32 {{.*}} 5) | |
| // CHECK: ret void | |
| { | |
| int arr[] = {1, 2, 3}; | |
| defer { | |
| x(5); | |
| } | |
| for (unsigned i = 0; i < 3; ++i) | |
| defer x(arr[i]); | |
| x(4); | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @sequencing_3() | |
| void sequencing_3() { | |
| // CHECK: entry: | |
| // CHECK: %r = alloca i32, align 4 | |
| // CHECK: store i32 0, ptr %r, align 4 | |
| // CHECK: %0 = load i32, ptr %r, align 4 | |
| // CHECK: %add = add nsw i32 %0, 1 | |
| // CHECK: store i32 %add, ptr %r, align 4 | |
| // CHECK: %1 = load i32, ptr %r, align 4 | |
| // CHECK: %mul = mul nsw i32 %1, 2 | |
| // CHECK: store i32 %mul, ptr %r, align 4 | |
| // CHECK: %2 = load i32, ptr %r, align 4 | |
| // CHECK: %add1 = add nsw i32 %2, 3 | |
| // CHECK: store i32 %add1, ptr %r, align 4 | |
| // CHECK: %3 = load i32, ptr %r, align 4 | |
| // CHECK: %mul2 = mul nsw i32 %3, 4 | |
| // CHECK: store i32 %mul2, ptr %r, align 4 | |
| // CHECK: ret void | |
| int r = 0; | |
| { | |
| defer { | |
| defer r *= 4; | |
| r *= 2; | |
| defer { | |
| r += 3; | |
| } | |
| } | |
| defer r += 1; | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @defer_stmt(i32 {{.*}} %q) | |
| void defer_stmt(int q) { | |
| // CHECK: entry: | |
| // CHECK: %q.addr = alloca i32, align 4 | |
| // CHECK: store i32 %q, ptr %q.addr, align 4 | |
| // CHECK: %0 = load i32, ptr %q.addr, align 4 | |
| // CHECK: %cmp = icmp eq i32 %0, 3 | |
| // CHECK: br i1 %cmp, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK: call void @x(i32 {{.*}} 42) | |
| // CHECK: br label %if.end | |
| // CHECK: if.end: | |
| // CHECK: ret void | |
| defer if (q == 3) x(42); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @defer_defer() | |
| void defer_defer() { | |
| // CHECK: entry: | |
| // CHECK: call void @x(i32 {{.*}} 0) | |
| // CHECK: call void @x(i32 {{.*}} 1) | |
| // CHECK: call void @x(i32 {{.*}} 2) | |
| // CHECK: call void @x(i32 {{.*}} 3) | |
| // CHECK: call void @x(i32 {{.*}} 4) | |
| // CHECK: ret void | |
| defer x(4); | |
| defer defer x(3); | |
| defer defer defer x(2); | |
| defer defer defer defer x(1); | |
| x(0); | |
| } | |
| // CHECK-LABEL: define {{.*}} i32 @vla(ptr {{.*}} %p, i32 {{.*}} %x) | |
| int vla(int* p, int x) { | |
| // CHECK: entry: | |
| // CHECK: %retval = alloca i32, align 4 | |
| // CHECK: %p.addr = alloca ptr, align 8 | |
| // CHECK: %x.addr = alloca i32, align 4 | |
| // CHECK: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK: %saved_stack = alloca ptr, align 8 | |
| // CHECK: %__vla_expr0 = alloca i64, align 8 | |
| // CHECK: %saved_stack3 = alloca ptr, align 8 | |
| // CHECK: %__vla_expr1 = alloca i64, align 8 | |
| // CHECK: store ptr %p, ptr %p.addr, align 8 | |
| // CHECK: store i32 %x, ptr %x.addr, align 4 | |
| // CHECK: %0 = load i32, ptr %x.addr, align 4 | |
| // CHECK: %cmp = icmp slt i32 %0, 5 | |
| // CHECK: br i1 %cmp, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK: store i32 10, ptr %retval, align 4 | |
| // CHECK: store i32 1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: br label %cleanup | |
| // CHECK: if.end: | |
| // CHECK: store i32 7, ptr %retval, align 4 | |
| // CHECK: store i32 1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %1 = load i32, ptr %x.addr, align 4 | |
| // CHECK: %2 = zext i32 %1 to i64 | |
| // CHECK: %3 = call ptr @llvm.stacksave.p0() | |
| // CHECK: store ptr %3, ptr %saved_stack, align 8 | |
| // CHECK: %vla = alloca i32, i64 %2, align 16 | |
| // CHECK: store i64 %2, ptr %__vla_expr0, align 8 | |
| // CHECK: %arrayidx = getelementptr inbounds i32, ptr %vla, i64 2 | |
| // CHECK: store i32 4, ptr %arrayidx, align 8 | |
| // CHECK: %arrayidx1 = getelementptr inbounds i32, ptr %vla, i64 2 | |
| // CHECK: %4 = load i32, ptr %arrayidx1, align 8 | |
| // CHECK: %5 = load ptr, ptr %p.addr, align 8 | |
| // CHECK: store i32 %4, ptr %5, align 4 | |
| // CHECK: %6 = load ptr, ptr %saved_stack, align 8 | |
| // CHECK: call void @llvm.stackrestore.p0(ptr %6) | |
| // CHECK: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: br label %cleanup | |
| // CHECK: cleanup: | |
| // CHECK: %cleanup.dest.saved2 = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %7 = load i32, ptr %x.addr, align 4 | |
| // CHECK: %8 = zext i32 %7 to i64 | |
| // CHECK: %9 = call ptr @llvm.stacksave.p0() | |
| // CHECK: store ptr %9, ptr %saved_stack3, align 8 | |
| // CHECK: %vla4 = alloca i32, i64 %8, align 16 | |
| // CHECK: store i64 %8, ptr %__vla_expr1, align 8 | |
| // CHECK: %arrayidx5 = getelementptr inbounds i32, ptr %vla4, i64 2 | |
| // CHECK: store i32 3, ptr %arrayidx5, align 8 | |
| // CHECK: %arrayidx6 = getelementptr inbounds i32, ptr %vla4, i64 2 | |
| // CHECK: %10 = load i32, ptr %arrayidx6, align 8 | |
| // CHECK: %11 = load ptr, ptr %p.addr, align 8 | |
| // CHECK: store i32 %10, ptr %11, align 4 | |
| // CHECK: %12 = load ptr, ptr %saved_stack3, align 8 | |
| // CHECK: call void @llvm.stackrestore.p0(ptr %12) | |
| // CHECK: store i32 %cleanup.dest.saved2, ptr %cleanup.dest.slot, align 4 | |
| // CHECK: %13 = load i32, ptr %retval, align 4 | |
| // CHECK: ret i32 %13 | |
| defer { | |
| int a[x]; | |
| a[2] = 3; | |
| *p = a[2]; | |
| } | |
| if (x < 5) { return 10; } | |
| defer { | |
| int b[x]; | |
| b[2] = 4; | |
| *p = b[2]; | |
| } | |
| return 7; | |
| } | |
| [[noreturn]] void exit(); | |
| [[noreturn]] void _Exit(); | |
| [[noreturn]] void foobar(); | |
| // CHECK-LABEL: define {{.*}} i32 @call_exit() | |
| int call_exit() { | |
| // CHECK: entry: | |
| // CHECK: call void @exit() | |
| // CHECK: unreachable | |
| defer x(1); | |
| exit(); | |
| } | |
| // CHECK-LABEL: define {{.*}} i32 @call__Exit() | |
| int call__Exit() { | |
| // CHECK: entry: | |
| // CHECK: call void @_Exit() | |
| // CHECK: unreachable | |
| defer x(1); | |
| _Exit(); | |
| } | |
| // CHECK-LABEL: define {{.*}} i32 @call_foobar() | |
| int call_foobar() { | |
| // CHECK: entry: | |
| // CHECK: call void @foobar() | |
| // CHECK: unreachable | |
| defer x(1); | |
| foobar(); | |
| } | |
| // CHECK-LABEL: define {{.*}} i32 @main() | |
| int main() { | |
| // CHECK: entry: | |
| // CHECK: %retval = alloca i32, align 4 | |
| // CHECK: store i32 0, ptr %retval, align 4 | |
| // CHECK: store i32 5, ptr %retval, align 4 | |
| // CHECK: call void @x(i32 {{.*}} 42) | |
| // CHECK: %0 = load i32, ptr %retval, align 4 | |
| // CHECK: ret i32 %0 | |
| defer x(42); | |
| return 5; | |
| } | |
| // CHECK-LABEL: define {{.*}} void @t() | |
| // CHECK: entry: | |
| // CHECK-NEXT: %count = alloca i32, align 4 | |
| // CHECK-NEXT: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK-NEXT: store i32 0, ptr %count, align 4 | |
| // CHECK-NEXT: br label %target | |
| // CHECK: target: | |
| // CHECK-NEXT: %0 = load i32, ptr %count, align 4 | |
| // CHECK-NEXT: %inc = add nsw i32 %0, 1 | |
| // CHECK-NEXT: store i32 %inc, ptr %count, align 4 | |
| // CHECK-NEXT: %1 = load i32, ptr %count, align 4 | |
| // CHECK-NEXT: %cmp = icmp sle i32 %1, 2 | |
| // CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: if.end: | |
| // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: cleanup: | |
| // CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 1) | |
| // CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ | |
| // CHECK-NEXT: i32 0, label %cleanup.cont | |
| // CHECK-NEXT: i32 2, label %target | |
| // CHECK-NEXT: ] | |
| // CHECK: cleanup.cont: | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 2) | |
| // CHECK-NEXT: ret void | |
| // CHECK: unreachable: | |
| // CHECK-NEXT: unreachable | |
| void t() { | |
| int count = 0; | |
| { | |
| target: | |
| _Defer { x(1); } | |
| ++count; | |
| if (count <= 2) { | |
| goto target; | |
| } | |
| } | |
| x(2); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @stmt_expr() | |
| // CHECK: entry: | |
| // CHECK-NEXT: %tmp = alloca i32, align 4 | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 1) | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 2) | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 3) | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 4) | |
| // CHECK-NEXT: store i32 6, ptr %tmp, align 4 | |
| // CHECK-NEXT: call void @x(i32 {{.*}} 5) | |
| // CHECK-NEXT: %0 = load i32, ptr %tmp, align 4 | |
| // CHECK-NEXT: call void @x(i32 {{.*}} %0) | |
| // CHECK-NEXT: ret void | |
| void stmt_expr() { | |
| ({ | |
| _Defer x(4); | |
| _Defer ({ | |
| _Defer x(3); | |
| x(2); | |
| }); | |
| x(1); | |
| }); | |
| x(({ | |
| _Defer x(5); | |
| 6; | |
| })); | |
| } | |
| // CHECK-LABEL: define {{.*}} void @cleanup_no_insert_point() | |
| // CHECK: entry: | |
| // CHECK-NEXT: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK-NEXT: br label %while.cond | |
| // CHECK: while.cond: | |
| // CHECK-NEXT: %call = call {{.*}} i1 @q(i32 {{.*}} 1) | |
| // CHECK-NEXT: br i1 %call, label %while.body, label %while.end | |
| // CHECK: while.body: | |
| // CHECK-NEXT: %call1 = call {{.*}} i1 @q(i32 {{.*}} 2) | |
| // CHECK-NEXT: br i1 %call1, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: if.end: | |
| // CHECK-NEXT: %call2 = call {{.*}} i1 @q(i32 {{.*}} 3) | |
| // CHECK-NEXT: br i1 %call2, label %if.then3, label %if.end4 | |
| // CHECK: if.then3: | |
| // CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: if.end4: | |
| // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: cleanup: | |
| // CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: call void @noreturn() | |
| // CHECK-NEXT: unreachable | |
| // CHECK: 0: | |
| // CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ | |
| // CHECK-NEXT: i32 0, label %cleanup.cont | |
| // CHECK-NEXT: i32 2, label %while.cond | |
| // CHECK-NEXT: i32 3, label %while.end | |
| // CHECK-NEXT: ] | |
| // CHECK: cleanup.cont: | |
| // CHECK-NEXT: br label %while.cond | |
| // CHECK: while.end: | |
| // CHECK-NEXT: ret void | |
| // CHECK: unreachable: | |
| // CHECK-NEXT: unreachable | |
| void cleanup_no_insert_point() { | |
| while (q(1)) { | |
| _Defer { | |
| noreturn(); | |
| }; | |
| if (q(2)) continue; | |
| if (q(3)) break; | |
| } | |
| } | |
| // CHECK-LABEL: define {{.*}} void @cleanup_nested() | |
| // CHECK: entry: | |
| // CHECK-NEXT: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK-NEXT: br label %while.cond | |
| // CHECK: while.cond: | |
| // CHECK-NEXT: %call = call {{.*}} i1 @q(i32 {{.*}} 1) | |
| // CHECK-NEXT: br i1 %call, label %while.body, label %while.end19 | |
| // CHECK: while.body: | |
| // CHECK-NEXT: %call1 = call {{.*}} i1 @q(i32 {{.*}} 6) | |
| // CHECK-NEXT: br i1 %call1, label %if.then, label %if.end | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: if.end: | |
| // CHECK-NEXT: %call2 = call {{.*}} i1 @q(i32 {{.*}} 7) | |
| // CHECK-NEXT: br i1 %call2, label %if.then3, label %if.end4 | |
| // CHECK: if.then3: | |
| // CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: if.end4: | |
| // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| // CHECK: cleanup: | |
| // CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %while.cond5 | |
| // CHECK: while.cond5: | |
| // CHECK-NEXT: %call6 = call {{.*}} i1 @q(i32 {{.*}} 2) | |
| // CHECK-NEXT: br i1 %call6, label %while.body7, label %while.end | |
| // CHECK: while.body7: | |
| // CHECK-NEXT: %call8 = call {{.*}} i1 @q(i32 {{.*}} 4) | |
| // CHECK-NEXT: br i1 %call8, label %if.then9, label %if.end10 | |
| // CHECK: if.then9: | |
| // CHECK-NEXT: store i32 4, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup14 | |
| // CHECK: if.end10: | |
| // CHECK-NEXT: %call11 = call {{.*}} i1 @q(i32 {{.*}} 5) | |
| // CHECK-NEXT: br i1 %call11, label %if.then12, label %if.end13 | |
| // CHECK: if.then12: | |
| // CHECK-NEXT: store i32 5, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup14 | |
| // CHECK: if.end13: | |
| // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup14 | |
| // CHECK: cleanup14: | |
| // CHECK-NEXT: %cleanup.dest.saved15 = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: %call16 = call {{.*}} i1 @q(i32 {{.*}} 3) | |
| // CHECK-NEXT: store i32 %cleanup.dest.saved15, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ | |
| // CHECK-NEXT: i32 0, label %cleanup.cont | |
| // CHECK-NEXT: i32 4, label %while.cond5 | |
| // CHECK-NEXT: i32 5, label %while.end | |
| // CHECK-NEXT: ] | |
| // CHECK: cleanup.cont: | |
| // CHECK-NEXT: br label %while.cond5 | |
| // CHECK: while.end: | |
| // CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: %cleanup.dest17 = load i32, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest17, label %unreachable [ | |
| // CHECK-NEXT: i32 0, label %cleanup.cont18 | |
| // CHECK-NEXT: i32 2, label %while.cond | |
| // CHECK-NEXT: i32 3, label %while.end19 | |
| // CHECK-NEXT: ] | |
| // CHECK: cleanup.cont18: | |
| // CHECK-NEXT: br label %while.cond | |
| // CHECK: while.end19: | |
| // CHECK-NEXT: ret void | |
| // CHECK: unreachable: | |
| // CHECK-NEXT: unreachable | |
| void cleanup_nested() { | |
| while (q(1)) { | |
| _Defer { | |
| while (q(2)) { | |
| _Defer { | |
| q(3); | |
| } | |
| if (q(4)) continue; | |
| if (q(5)) break; | |
| } | |
| }; | |
| if (q(6)) continue; | |
| if (q(7)) break; | |
| } | |
| } |