Files
TheRock/compiler/amd-llvm/clang/test/CIR/Transforms/flatten-cleanup-scope-simple.cir
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

689 lines
26 KiB
Plaintext

// RUN: cir-opt %s -cir-flatten-cfg -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s
!s32i = !cir.int<s, 32>
!rec_SomeClass = !cir.struct<"SomeClass" {!s32i}>
// Test basic cleanup scope flattening with normal cleanup.
// The body yields to cleanup, cleanup yields to continue.
cir.func @test_normal_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_normal_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CONTINUE:bb[0-9]+]]
// CHECK: ^[[CONTINUE]]:
// CHECK: cir.return
// Test that a single return from the cleanup body branches through the cleanup.
cir.func @test_return_from_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.return // Should branch through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_return_from_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_EXIT:bb[0-9]+]]
// CHECK: ^[[CLEANUP_EXIT]]:
// CHECK: cir.return
// CHECK: ^[[UNREACHABLE_NORMAL_RETURN:bb[0-9]+]]
// CHECK: cir.return
// Test that a single return from the cleanup body branches through the cleanup.
cir.func @test_return_from_cleanup_with_operand() -> !s32i {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
%1 = cir.const #cir.int<-1> : !s32i
cir.return %1 : !s32i // Should branch through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
%2 = cir.const #cir.int<0> : !s32i
cir.return %2 : !s32i
}
// CHECK-LABEL: cir.func @test_return_from_cleanup_with_operand()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_EXIT:bb[0-9]+]]
// CHECK: ^[[CLEANUP_EXIT]]:
// CHECK: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1> : !s32i
// CHECK: cir.return %[[MINUS_ONE]] : !s32i
// CHECK: ^[[UNREACHABLE_NORMAL_RETURN:bb[0-9]+]]
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.return %[[ZERO]] : !s32i
// Test that a continue statement in a cleanup scope branches through the
// cleanup block before continuing the loop.
cir.func @test_continue_in_cleanup_in_loop() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%cont = cir.call @shouldContinue() : () -> !cir.bool
cir.brcond %cont ^bb_continue, ^bb_normal
^bb_continue:
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.continue // Branches through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.br ^bb_normal
^bb_normal:
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_continue_in_cleanup_in_loop()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[COND1:.*]] = cir.call @shouldContinue() : () -> !cir.bool
// CHECK: cir.brcond %[[COND1]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[COND2:.*]] = cir.call @shouldContinue() : () -> !cir.bool
// CHECK: cir.brcond %[[COND2]] ^[[CONTINUE_PATH:bb[0-9]+]], ^[[NORMAL_PATH:bb[0-9]+]]
// CHECK: ^[[CONTINUE_PATH]]:
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_EXIT:bb[0-9]+]]
// CHECK: ^[[CLEANUP_EXIT]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[NORMAL_PATH]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test that a break statement in a cleanup scope branches through the
// cleanup block before breaking out of the loop.
cir.func @test_break_in_cleanup_in_loop() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%cont = cir.call @shouldContinue() : () -> !cir.bool
cir.brcond %cont ^bb_continue, ^bb_normal
^bb_continue:
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.break // Branches through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.br ^bb_normal
^bb_normal:
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_break_in_cleanup_in_loop()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[COND1:.*]] = cir.call @shouldContinue() : () -> !cir.bool
// CHECK: cir.brcond %[[COND1]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[COND2:.*]] = cir.call @shouldContinue() : () -> !cir.bool
// CHECK: cir.brcond %[[COND2]] ^[[CONTINUE_PATH:bb[0-9]+]], ^[[NORMAL_PATH:bb[0-9]+]]
// CHECK: ^[[CONTINUE_PATH]]:
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_EXIT:bb[0-9]+]]
// CHECK: ^[[CLEANUP_EXIT]]:
// CHECK: cir.br ^[[LOOP_EXIT]]
// CHECK: ^[[NORMAL_PATH]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test nested cleanup scopes.
cir.func @test_nested_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init] {alignment = 4 : i64}
%1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @ctor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
} cleanup normal {
cir.call @dtor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_nested_cleanup()
// CHECK: %[[ALLOCA1:.*]] = cir.alloca !rec_SomeClass
// CHECK: %[[ALLOCA2:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA1]])
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA2]])
// CHECK: cir.br ^[[AFTER_INNER:bb[0-9]+]]
// CHECK: ^[[AFTER_INNER]]:
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA1]])
// CHECK: cir.br ^[[CONTINUE:bb[0-9]+]]
// CHECK: ^[[CONTINUE]]:
// CHECK: cir.return
// Test a switch within a cleanup scope where the switch does not branch
// through the cleanup.
cir.func @test_normal_cleanup_with_switch() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%1 = cir.call @get() : () -> !s32i
cir.switch (%1 : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.break // Break from switch -- no cleanup exit
}
cir.case (equal, [#cir.int<2> : !s32i]) {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.break // Break from switch -- no cleanup exit
}
cir.yield // Yield from switch -- no cleanup exit
}
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_normal_cleanup_with_switch()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: %[[X:.*]] = cir.call @get() : () -> !s32i
// CHECK: cir.br ^[[SWITCH:bb[0-9]+]]
// CHECK: ^[[SWITCH]]:
// CHECK: cir.switch.flat %[[X]] : !s32i, ^bb6 [
// CHECK: 1: ^[[BREAK:bb[0-9]+]],
// CHECK: 2: ^[[CALL:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[FALLTHROUGH:bb[0-9]+]]:
// CHECK: cir.br ^[[BREAK]]
// CHECK: ^[[BREAK]]:
// CHECK: cir.br ^[[SWITCH_END:bb[0-9]+]]
// CHECK: ^[[CALL]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[SWITCH_END]]
// CHECK: ^[[SWITCH_END]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test a loop within a cleanup scope where the loop's break and continue are
// handled by the loop itself, so only the yield after the loop exits through
// the cleanup.
cir.func @test_loop_in_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%brk = cir.call @shouldBreak() : () -> !cir.bool
cir.brcond %brk ^bb_break, ^bb_continue
^bb_break:
cir.break // Break from loop - handled by loop, no cleanup exit
^bb_continue:
cir.continue // Continue loop - handled by loop, no cleanup exit
}
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_loop_in_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[LOOP_BEGIN:bb[0-9]+]]
// CHECK: ^[[LOOP_BEGIN]]:
// CHECK: cir.br ^[[COND:bb[0-9]+]]
// CHECK: ^[[COND]]:
// CHECK: %[[SHOULDCONT:.*]] = cir.call @shouldContinue
// CHECK: cir.brcond %[[SHOULDCONT]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[SHOULDBRK:.*]] = cir.call @shouldBreak
// CHECK: cir.brcond %[[SHOULDBRK]] ^[[BREAK:bb[0-9]+]], ^[[CONT:bb[0-9]+]]
// CHECK: ^[[BREAK]]:
// CHECK: cir.br ^[[LOOP_EXIT]]
// CHECK: ^[[CONT]]:
// CHECK: cir.br ^[[COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test a switch within a loop within a cleanup scope. The switch's break is
// handled by the switch, the loop's break/continue are handled by the loop,
// so only the yield exits through cleanup.
cir.func @test_switch_in_loop_in_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%x = cir.call @get() : () -> !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.break // Break from switch - handled by switch
}
cir.case (equal, [#cir.int<2> : !s32i]) {
cir.continue // Continue outer loop - handled by loop
}
cir.yield
}
cir.yield // Yield from loop body
}
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_switch_in_loop_in_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[LOOP_BEGIN:bb[0-9]+]]
// CHECK: ^[[LOOP_BEGIN]]:
// CHECK: cir.br ^[[COND:bb[0-9]+]]
// CHECK: ^[[COND]]:
// CHECK: %[[SHOULDCONT:.*]] = cir.call @shouldContinue
// CHECK: cir.brcond %[[SHOULDCONT]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[X:.*]] = cir.call @get() : () -> !s32i
// CHECK: cir.br ^[[SWITCH_BEGIN:bb[0-9]+]]
// CHECK: ^[[SWITCH_BEGIN]]:
// CHECK: cir.switch.flat %[[X]] : !s32i, ^[[FALLTHROUGH:bb[0-9]+]] [
// CHECK: 1: ^[[SWITCH_BREAK:bb[0-9]+]],
// CHECK: 2: ^[[SWITCH_CONTINUE:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[SWITCH_BREAK]]:
// CHECK: cir.br ^[[FALLTHROUGH]]
// CHECK: ^[[SWITCH_CONTINUE]]:
// CHECK: cir.br ^[[COND]]
// CHECK: ^[[FALLTHROUGH]]:
// CHECK: cir.br ^[[COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test a loop within a switch within a cleanup scope. The loop's break/continue
// are handled by the loop, the switch's break exits the switch, so only the
// yield exits through cleanup.
cir.func @test_loop_in_switch_in_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%x = cir.call @get() : () -> !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%brk = cir.call @shouldBreak() : () -> !cir.bool
cir.brcond %brk ^bb_break, ^bb_continue
^bb_break:
cir.break // Break from loop - handled by loop
^bb_continue:
cir.continue // Continue loop - handled by loop
}
cir.break // Break from switch - handled by switch
}
cir.yield
}
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_loop_in_switch_in_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: %[[X:.*]] = cir.call @get() : () -> !s32i
// CHECK: cir.br ^[[SWITCH_BEGIN:bb[0-9]+]]
// CHECK: ^[[SWITCH_BEGIN]]:
// CHECK: cir.switch.flat %[[X]] : !s32i, ^[[SWITCH_END:bb[0-9]+]] [
// CHECK: 1: ^[[CASE1:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[FALLTHROUGH:bb[0-9]+]]:
// CHECK: cir.br ^[[CASE1]]
// CHECK: ^[[CASE1]]:
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[SHOULDCONT:.*]] = cir.call @shouldContinue() : () -> !cir.bool
// CHECK: cir.brcond %[[SHOULDCONT]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[SHOULDBRK:.*]] = cir.call @shouldBreak() : () -> !cir.bool
// CHECK: cir.brcond %[[SHOULDBRK]] ^[[BREAK:bb[0-9]+]], ^[[CONT:bb[0-9]+]]
// CHECK: ^[[BREAK]]:
// CHECK: cir.br ^[[LOOP_EXIT]]
// CHECK: ^[[CONT]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.br ^[[SWITCH_END]]
// CHECK: ^[[SWITCH_END]]:
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test a cleanup scope containing a switch which contains a nested cleanup
// scope with a break. The break is handled by the switch, so the outer cleanup
// has only one exit (yield) and should flatten successfully.
cir.func @test_nested_cleanup_break_in_switch() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init] {alignment = 4 : i64}
%1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%x = cir.call @get() : () -> !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.call @ctor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.break // Branch through inner cleanup, but not outer cleanup
} cleanup normal {
cir.call @dtor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.break
}
cir.yield
}
cir.yield // Only exit from outer cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_nested_cleanup_break_in_switch()
// CHECK: %[[ALLOCA1:.*]] = cir.alloca !rec_SomeClass
// CHECK: %[[ALLOCA2:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA1]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: %[[X:.*]] = cir.call @get() : () -> !s32i
// CHECK: cir.br ^[[SWITCH_BEGIN:bb[0-9]+]]
// CHECK: ^[[SWITCH_BEGIN]]:
// CHECK: cir.switch.flat %[[X]] : !s32i, ^[[SWITCH_END:bb[0-9]+]] [
// CHECK: 1: ^[[CASE1:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[CASE1]]:
// CHECK: cir.call @ctor(%[[ALLOCA2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA2]])
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA2]])
// CHECK: cir.br ^[[EXIT_CLEANUP:bb[0-9]+]]
// CHECK: ^[[EXIT_CLEANUP]]:
// CHECK: cir.br ^[[SWITCH_END]]
// CHECK: ^[[SWITCH_END]]:
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA1]])
// CHECK: cir.br ^[[OUTER_CLEANUP_DONE:bb[0-9]+]]
// CHECK: ^[[OUTER_CLEANUP_DONE]]:
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test that a goto inside the cleanup body that targets a label also inside
// the cleanup body is left alone by flattening. The cleanup's only exit is
// the yield at the end of the body, so the cleanup runs once after the goto's
// destination is reached.
cir.func @test_goto_within_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%cond = cir.call @shouldGoto() : () -> !cir.bool
cir.brcond %cond ^bb_goto, ^bb_other
^bb_goto:
cir.goto "internal_label"
^bb_other:
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.br ^bb_label
^bb_label:
cir.label "internal_label"
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_goto_within_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldGoto
// CHECK: cir.brcond %[[COND]] ^[[GOTO_BB:bb[0-9]+]], ^[[OTHER_BB:bb[0-9]+]]
// CHECK: ^[[GOTO_BB]]:
// CHECK: cir.goto "internal_label"
// CHECK: ^[[OTHER_BB]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[LABEL_BB:bb[0-9]+]]
// CHECK: ^[[LABEL_BB]]:
// CHECK: cir.label "internal_label"
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DONE:bb[0-9]+]]
// CHECK: ^[[DONE]]:
// CHECK: cir.return
// Test a goto inside a nested cleanup scope whose target label is inside the
// same nested cleanup body. Neither cleanup needs to handle the goto as an
// exit; the inner cleanup runs once after the goto's destination is reached
// and the outer cleanup runs once on normal exit from the inner cleanup.
cir.func @test_goto_within_nested_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init] {alignment = 4 : i64}
%1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @ctor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%cond = cir.call @shouldGoto() : () -> !cir.bool
cir.brcond %cond ^bb_goto, ^bb_other
^bb_goto:
cir.goto "inner_label"
^bb_other:
cir.call @doSomething(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.br ^bb_label
^bb_label:
cir.label "inner_label"
cir.yield
} cleanup normal {
cir.call @dtor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_goto_within_nested_cleanup()
// CHECK: %[[A1:.*]] = cir.alloca !rec_SomeClass{{.*}}["c1"
// CHECK: %[[A2:.*]] = cir.alloca !rec_SomeClass{{.*}}["c2"
// CHECK: cir.call @ctor(%[[A1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[A2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldGoto
// CHECK: cir.brcond %[[COND]] ^[[GOTO_BB:bb[0-9]+]], ^[[OTHER_BB:bb[0-9]+]]
// CHECK: ^[[GOTO_BB]]:
// CHECK: cir.goto "inner_label"
// CHECK: ^[[OTHER_BB]]:
// CHECK: cir.call @doSomething(%[[A2]])
// CHECK: cir.br ^[[LABEL_BB:bb[0-9]+]]
// CHECK: ^[[LABEL_BB]]:
// CHECK: cir.label "inner_label"
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[A2]])
// CHECK: cir.br ^[[INNER_DONE:bb[0-9]+]]
// CHECK: ^[[INNER_DONE]]:
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[A1]])
// CHECK: cir.br ^[[OUTER_DONE:bb[0-9]+]]
// CHECK: ^[[OUTER_DONE]]:
// CHECK: cir.return
// Test that a single goto out of a cleanup scope branches through the
// cleanup before transferring control to its target label. After the cleanup
// body branches to cleanup entry, the cleanup runs and then a goto with the
// same target label is emitted in the cleanup continuation block.
cir.func @test_goto_out_of_cleanup() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.call @doSomething(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.goto "exit"
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.br ^bb1
^bb1:
cir.label "exit"
cir.return
}
// CHECK-LABEL: cir.func @test_goto_out_of_cleanup()
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[BODY:bb[0-9]+]]
// CHECK: ^[[BODY]]:
// CHECK: cir.call @doSomething(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_EXIT:bb[0-9]+]]
// CHECK: ^[[CLEANUP_EXIT]]:
// CHECK: cir.goto "exit"
// CHECK: ^[[UNREACHABLE_FALLTHROUGH:bb[0-9]+]]:
// CHECK: cir.br ^[[LABEL_BB:bb[0-9]+]]
// CHECK: ^[[LABEL_BB]]:
// CHECK: cir.label "exit"
// CHECK: cir.return
cir.func private @get() -> !s32i
cir.func private @ctor(!cir.ptr<!rec_SomeClass>)
cir.func private @dtor(!cir.ptr<!rec_SomeClass>)
cir.func private @doSomething(!cir.ptr<!rec_SomeClass>)
cir.func private @shouldBreak() -> !cir.bool
cir.func private @shouldContinue() -> !cir.bool
cir.func private @shouldGoto() -> !cir.bool