| // 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; |
| } |
| } |