Files
TheRock/compiler/amd-llvm/clang/test/CIR/Transforms/flatten-cleanup-scope-multi-exit.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

1283 lines
55 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 that a cleanup scope with break that branches through cleanup is
// properly flattened with a destination slot and switch dispatch.
cir.func @test_multi_exit_with_break() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%cond = cir.call @shouldBreak() : () -> !cir.bool
cir.brcond %cond ^bb_break, ^bb_normal
^bb_break:
cir.break // Break through cleanup - exits loop
^bb_normal:
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_multi_exit_with_break()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldBreak()
// CHECK: cir.brcond %[[COND]] ^[[BREAK_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[BREAK_PATH]]:
// CHECK: %[[BREAK_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[BREAK_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[BREAK_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[BREAK_DEST]]:
// CHECK: cir.br ^[[LOOP_EXIT]]
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
cir.func private @ctor(!cir.ptr<!rec_SomeClass>)
cir.func private @dtor(!cir.ptr<!rec_SomeClass>)
cir.func private @shouldBreak() -> !cir.bool
// Test that a cleanup scope with continue that branches through cleanup is
// properly flattened.
cir.func @test_multi_exit_with_continue() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.brcond %cond ^bb_continue, ^bb_normal
^bb_continue:
cir.continue // Continue through cleanup
^bb_normal:
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_multi_exit_with_continue()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldContinue()
// CHECK: cir.brcond %[[COND]] ^[[CONTINUE_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[CONTINUE_PATH]]:
// CHECK: %[[CONTINUE_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[CONTINUE_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[CONTINUE_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[CONTINUE_DEST]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
cir.func private @shouldContinue() -> !cir.bool
// Test continue inside a switch that's inside a cleanup scope.
cir.func @test_continue_in_switch() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%x = cir.const #cir.int<1> : !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.break // Break from switch -- no cleanup exit
}
cir.case (equal, [#cir.int<2> : !s32i]) {
cir.continue // Continue to outer loop - exits through cleanup!
}
cir.yield
}
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_continue_in_switch()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.br ^[[SWITCH_ENTRY:bb[0-9]+]]
// CHECK: ^[[SWITCH_ENTRY]]:
// CHECK: cir.switch.flat %[[ONE]] : !s32i, ^[[SWITCH_DEFAULT:bb[0-9]+]] [
// CHECK: 1: ^[[CASE_ONE:bb[0-9]+]],
// CHECK: 2: ^[[CASE_TWO:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[CASE_ONE]]:
// CHECK: cir.br ^[[SWITCH_DEFAULT]]
// CHECK: ^[[CASE_TWO]]:
// CHECK: %[[CONTINUE_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[CONTINUE_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[SWITCH_DEFAULT]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[CONTINUE_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[CONTINUE_DEST]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test return inside a loop inside a switch inside a cleanup scope.
cir.func @test_return_in_loop_in_switch() {
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
cir.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%x = cir.const #cir.int<1> : !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
// Nested loop inside switch
cir.while {
%cond = cir.call @shouldContinue() : () -> !cir.bool
cir.condition(%cond)
} do {
%ret = cir.call @shouldReturn() : () -> !cir.bool
cir.brcond %ret ^bb_return, ^bb_continue
^bb_return:
cir.return // Return inside loop inside switch - exits through cleanup!
^bb_continue:
cir.yield
}
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.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_return_in_loop_in_switch()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor
// CHECK: cir.br ^[[CLEANUP_SCOPE_ENTRY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_SCOPE_ENTRY]]:
// CHECK: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.br ^[[SWITCH_ENTRY:bb[0-9]+]]
// CHECK: ^[[SWITCH_ENTRY]]:
// CHECK: cir.switch.flat %[[ONE]] : !s32i, ^[[SWITCH_FALLTHROUGH:bb[0-9]+]] [
// CHECK: 1: ^[[CASE_ONE:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[CASE_ONE]]:
// CHECK: cir.br ^[[INNER_LOOP_COND:bb[0-9]+]]
// CHECK: ^[[INNER_LOOP_COND]]:
// CHECK: %[[SHOULD_CONTINUE:.*]] = cir.call @shouldContinue()
// CHECK: cir.brcond %[[SHOULD_CONTINUE]] ^[[INNER_LOOP_BODY:bb[0-9]+]], ^[[INNER_LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[INNER_LOOP_BODY]]:
// CHECK: %[[SHOULD_RETURN:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[SHOULD_RETURN]] ^[[RETURN_FROM_INNER:bb[0-9]+]], ^[[NORMAL_PATH:bb[0-9]+]]
// CHECK: ^[[RETURN_FROM_INNER]]:
// CHECK: %[[RETURN_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RETURN_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP_ENTRY:bb[0-9]+]]
// CHECK: ^[[NORMAL_PATH]]:
// CHECK: cir.br ^[[INNER_LOOP_COND]]
// CHECK: ^[[INNER_LOOP_EXIT]]:
// CHECK: cir.br ^[[SWITCH_EXIT:bb[0-9]+]]
// CHECK: ^[[SWITCH_EXIT]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP_ENTRY]]
// CHECK: ^[[CLEANUP_ENTRY]]:
// CHECK: cir.call @dtor
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_LOOP_BODY_DONE:bb[0-9]+]]
// CHECK: ^[[RET_DEST]]:
// CHECK: cir.return
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_LOOP_BODY_DONE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
cir.func private @shouldReturn() -> !cir.bool
// Test return inside nested cleanup scopes - both need multi-exit handling.
cir.func @test_return_in_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 @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_normal
^bb_return:
cir.return // Return exits through BOTH cleanup scopes
^bb_normal:
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_return_in_nested_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1"
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2"
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_RET_DEST]]:
// CHECK: %[[OUTER_RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[OUTER_RET_DEST]]:
// CHECK: cir.return
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: cir.return
// Test return inside nested cleanup scopes.
cir.func @test_return_constant_in_nested_cleanup() -> !s32i {
%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 @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_normal
^bb_return:
%2 = cir.const #cir.int<-1> : !s32i
cir.return %2 : !s32i // Return exits through BOTH cleanup scopes
^bb_normal:
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
}
%3 = cir.const #cir.int<0> : !s32i
cir.return %3 : !s32i
}
// CHECK-LABEL: cir.func @test_return_constant_in_nested_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init]
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_RET_DEST]]:
// CHECK: %[[OUTER_RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[OUTER_RET_DEST]]:
// CHECK: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1> : !s32i
// CHECK: cir.return %[[MINUS_ONE]] : !s32i
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.return %[[ZERO]]
// Test return with computed value.
cir.func @test_return_computed_value(%arg0: !s32i) -> !s32i {
%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 @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_normal
^bb_return:
%retval = cir.not %arg0 : !s32i
cir.return %retval : !s32i
^bb_normal:
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
}
%zero = cir.const #cir.int<0> : !s32i
cir.return %zero : !s32i
}
// CHECK-LABEL: cir.func @test_return_computed_value
// CHECK-SAME: (%[[ARG0:.*]]: !s32i)
// CHECK: %[[RET_TMP:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__ret_operand_tmp"]
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init]
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: %[[COMPUTED:.*]] = cir.not %[[ARG0]] : !s32i
// CHECK: cir.store %[[COMPUTED]], %[[RET_TMP]]
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_RET_DEST]]:
// CHECK: %[[OUTER_RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[OUTER_RET_DEST]]:
// CHECK: %[[LOADED_RET:.*]] = cir.load %[[RET_TMP]]
// CHECK: cir.return %[[LOADED_RET]] : !s32i
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.return %[[ZERO]]
// Test return with of a function parameter value.
cir.func @test_return_param_value(%arg0: !s32i) -> !s32i {
%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 @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_normal
^bb_return:
cir.return %arg0 : !s32i
^bb_normal:
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
}
%zero = cir.const #cir.int<0> : !s32i
cir.return %zero : !s32i
}
// CHECK-LABEL: cir.func @test_return_param_value
// CHECK-SAME: (%[[ARG0:.*]]: !s32i)
// CHECK: %[[RET_TMP:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__ret_operand_tmp"]
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init]
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: cir.store %[[ARG0]], %[[RET_TMP]]
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_RET_DEST]]:
// CHECK: %[[OUTER_RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[OUTER_RET_DEST]]:
// CHECK: %[[LOADED_RET:.*]] = cir.load %[[RET_TMP]]
// CHECK: cir.return %[[LOADED_RET]] : !s32i
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.return %[[ZERO]]
// Test return of a block argument value.
cir.func @test_return_blockarg_value(%arg0: !s32i) -> !s32i {
%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 @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_normal
^bb_return:
cir.br ^bb_return_arg(%arg0 : !s32i)
^bb_return_arg(%block_arg: !s32i):
cir.return %block_arg : !s32i
^bb_normal:
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
}
%zero = cir.const #cir.int<0> : !s32i
cir.return %zero : !s32i
}
// CHECK-LABEL: cir.func @test_return_blockarg_value
// CHECK-SAME: (%[[ARG0:.*]]: !s32i)
// CHECK: %[[RET_TMP:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__ret_operand_tmp"]
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1", init]
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: cir.br ^[[RET_ARG_PATH:.*]](%[[ARG0]] : !s32i)
// CHECK: ^[[RET_ARG_PATH]](%[[BLOCK_ARG:.*]]: !s32i):
// CHECK: cir.store %[[BLOCK_ARG]], %[[RET_TMP]]
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_RET_DEST]]:
// CHECK: %[[OUTER_RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[OUTER_RET_DEST]]:
// CHECK: %[[LOADED_RET:.*]] = cir.load %[[RET_TMP]]
// CHECK: cir.return %[[LOADED_RET]] : !s32i
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.return %[[ZERO]]
// Test break inside nested cleanup scopes within a loop.
cir.func @test_break_in_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.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
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 @shouldBreak() : () -> !cir.bool
cir.brcond %cond ^bb_break, ^bb_normal
^bb_break:
cir.break // Break exits through BOTH cleanup scopes, then exits loop
^bb_normal:
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.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_break_in_nested_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1"
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2"
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldBreak()
// CHECK: cir.brcond %[[COND]] ^[[BREAK_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[BREAK_PATH]]:
// CHECK: %[[BREAK_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[BREAK_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_BREAK_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_BREAK_DEST]]:
// CHECK: %[[OUTER_BREAK_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_BREAK_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_BREAK_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[OUTER_BREAK_DEST]]:
// CHECK: cir.br ^[[LOOP_EXIT]]
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test continue inside nested cleanup scopes within a loop.
cir.func @test_continue_in_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.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
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 @shouldContinue() : () -> !cir.bool
cir.brcond %cond ^bb_continue, ^bb_normal
^bb_continue:
cir.continue // Continue exits through BOTH cleanup scopes, then continues loop
^bb_normal:
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.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_continue_in_nested_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1"
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2"
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldContinue()
// CHECK: cir.brcond %[[COND]] ^[[CONTINUE_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[CONTINUE_PATH]]:
// CHECK: %[[CONTINUE_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[CONTINUE_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_CONTINUE_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_CONTINUE_DEST]]:
// CHECK: %[[OUTER_CONTINUE_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_CONTINUE_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_CONTINUE_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[OUTER_CONTINUE_DEST]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test continue inside nested cleanup in switch.
cir.func @test_continue_in_nested_cleanup_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}
%2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
cir.while {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
%x = cir.load %2 : !cir.ptr<!s32i>, !s32i
cir.switch (%x : !s32i) {
cir.case (equal, [#cir.int<1> : !s32i]) {
cir.call @ctor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.cleanup.scope {
cir.continue // Branches through both cleanups
} cleanup normal {
cir.call @dtor(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.break
}
cir.yield
}
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_continue_in_nested_cleanup_in_switch()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1"
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2"
// CHECK: %[[ALLOCA_X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: %[[X:.*]] = cir.load %[[ALLOCA_X]]
// CHECK: cir.br ^[[SWITCH_ENTRY:bb[0-9]+]]
// CHECK: ^[[SWITCH_ENTRY]]:
// CHECK: cir.switch.flat %[[X]] : !s32i, ^[[SWITCH_DEFAULT:bb[0-9]+]] [
// CHECK: 1: ^[[CASE_ONE:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[CASE_ONE]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_EXIT:bb[0-9]+]]
// CHECK: ^[[INNER_EXIT]]:
// CHECK: %[[CONTINUE_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[CONTINUE_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[SWITCH_DEFAULT]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[CONTINUE_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[LOOP_CONTINUE:bb[0-9]+]]
// CHECK: ^[[CONTINUE_DEST]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[LOOP_CONTINUE]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: cir.return
// Test return inside loop inside cleanup scope.
cir.func @test_return_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 {
%true = cir.const #cir.bool<true> : !cir.bool
cir.condition(%true)
} do {
%cond = cir.call @shouldReturn() : () -> !cir.bool
cir.brcond %cond ^bb_return, ^bb_continue
^bb_return:
cir.return // Return branches through cleanup
^bb_continue:
cir.yield
}
cir.yield
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.return
}
// CHECK-LABEL: cir.func @test_return_in_loop_in_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA:.*]] = cir.alloca !rec_SomeClass
// CHECK: cir.call @ctor(%[[ALLOCA]])
// CHECK: cir.br ^[[CLEANUP_BODY:bb[0-9]+]]
// CHECK: ^[[CLEANUP_BODY]]:
// CHECK: cir.br ^[[LOOP_COND:bb[0-9]+]]
// CHECK: ^[[LOOP_COND]]:
// CHECK: %[[TRUE:.*]] = cir.const #true
// CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[LOOP_EXIT:bb[0-9]+]]
// CHECK: ^[[LOOP_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldReturn()
// CHECK: cir.brcond %[[COND]] ^[[RET_PATH:bb[0-9]+]], ^[[CONTINUE_PATH:bb[0-9]+]]
// CHECK: ^[[RET_PATH]]:
// CHECK: %[[RET_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[RET_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[CONTINUE_PATH]]:
// CHECK: cir.br ^[[LOOP_COND]]
// CHECK: ^[[LOOP_EXIT]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[RET_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[FINAL_RET:bb[0-9]+]]
// CHECK: ^[[RET_DEST]]:
// CHECK: cir.return
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[FINAL_RET]]:
// CHECK: cir.return
cir.func private @shouldGoto() -> !cir.bool
// Test that a goto out of a cleanup scope shares the cleanup dispatch with
// a yield exit. The original goto is replaced by a store of its destination
// id and a branch to the cleanup entry; after the cleanup runs, the dispatch
// switch routes the goto's destination id to a block containing a new
// cir.goto to the same label.
cir.func @test_multi_exit_with_goto() {
%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_normal
^bb_goto:
cir.goto "exit" // Goto through cleanup
^bb_normal:
cir.yield // Normal exit through cleanup
} cleanup normal {
cir.call @dtor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
cir.yield
}
cir.br ^bb_end
^bb_end:
cir.label "exit"
cir.return
}
// CHECK-LABEL: cir.func @test_multi_exit_with_goto()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// 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_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[GOTO_PATH]]:
// CHECK: %[[GOTO_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[GOTO_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[CLEANUP]]
// CHECK: ^[[CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA]])
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]]
// CHECK: ^[[DISPATCH]]:
// CHECK: %[[SLOT_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[SLOT_VAL]] : !s32i, ^[[DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[GOTO_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[YIELD_DEST]]:
// CHECK: cir.br ^[[CONTINUE:bb[0-9]+]]
// CHECK: ^[[GOTO_DEST]]:
// CHECK: cir.goto "exit"
// CHECK: ^[[DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[CONTINUE]]:
// CHECK: cir.br ^[[LABEL_BB:bb[0-9]+]]
// CHECK: ^[[LABEL_BB]]:
// CHECK: cir.label "exit"
// CHECK: cir.return
// Test a goto inside nested cleanup scopes whose target label is outside both
// scopes. The goto must branch through both cleanups before reaching its
// target. Each cleanup re-emits the goto via its dispatch destination block.
cir.func @test_goto_in_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_normal
^bb_goto:
cir.goto "target" // Goto through BOTH cleanups
^bb_normal:
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.br ^bb_end
^bb_goto_target:
cir.label "target"
cir.br ^bb_end
^bb_end:
cir.return
}
// CHECK-LABEL: cir.func @test_goto_in_nested_cleanup()
// CHECK: %[[DEST_SLOT:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot", cleanup_dest_slot]
// CHECK: %[[ALLOCA_C1:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c1"
// CHECK: %[[ALLOCA_C2:.*]] = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2"
// CHECK: cir.call @ctor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_BODY:bb[0-9]+]]
// CHECK: ^[[OUTER_BODY]]:
// CHECK: cir.call @ctor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_BODY:bb[0-9]+]]
// CHECK: ^[[INNER_BODY]]:
// CHECK: %[[COND:.*]] = cir.call @shouldGoto()
// CHECK: cir.brcond %[[COND]] ^[[GOTO_PATH:bb[0-9]+]], ^[[YIELD_PATH:bb[0-9]+]]
// CHECK: ^[[GOTO_PATH]]:
// CHECK: %[[GOTO_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[GOTO_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[YIELD_PATH]]:
// CHECK: %[[YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[INNER_CLEANUP]]
// CHECK: ^[[INNER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C2]])
// CHECK: cir.br ^[[INNER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[INNER_DISPATCH]]:
// CHECK: %[[INNER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[INNER_VAL]] : !s32i, ^[[INNER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[INNER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[INNER_GOTO_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[INNER_YIELD_DEST]]:
// CHECK: cir.br ^[[OUTER_YIELD_STORE:bb[0-9]+]]
// CHECK: ^[[INNER_GOTO_DEST]]:
// CHECK: %[[OUTER_GOTO_ID:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store %[[OUTER_GOTO_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[INNER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[OUTER_YIELD_STORE]]:
// CHECK: %[[OUTER_YIELD_ID:.*]] = cir.const #cir.int<0> : !s32i
// CHECK: cir.store %[[OUTER_YIELD_ID]], %[[DEST_SLOT]]
// CHECK: cir.br ^[[OUTER_CLEANUP]]
// CHECK: ^[[OUTER_CLEANUP]]:
// CHECK: cir.call @dtor(%[[ALLOCA_C1]])
// CHECK: cir.br ^[[OUTER_DISPATCH:bb[0-9]+]]
// CHECK: ^[[OUTER_DISPATCH]]:
// CHECK: %[[OUTER_VAL:.*]] = cir.load %[[DEST_SLOT]]
// CHECK: cir.switch.flat %[[OUTER_VAL]] : !s32i, ^[[OUTER_DEFAULT:bb[0-9]+]] [
// CHECK: 0: ^[[OUTER_YIELD_DEST:bb[0-9]+]],
// CHECK: 1: ^[[OUTER_GOTO_DEST:bb[0-9]+]]
// CHECK: ]
// CHECK: ^[[OUTER_YIELD_DEST]]:
// CHECK: cir.br ^[[AFTER_CLEANUP:bb[0-9]+]]
// CHECK: ^[[OUTER_GOTO_DEST]]:
// CHECK: cir.goto "target"
// CHECK: ^[[OUTER_DEFAULT]]:
// CHECK: cir.unreachable
// CHECK: ^[[AFTER_CLEANUP]]:
// CHECK: cir.br ^[[END:bb[0-9]+]]
// CHECK: ^[[LABEL_BB:bb[0-9]+]]:
// CHECK: cir.label "target"
// CHECK: cir.br ^[[END]]
// CHECK: ^[[END]]:
// CHECK: cir.return