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
689 lines
26 KiB
Plaintext
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
|