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
664 lines
30 KiB
C++
664 lines
30 KiB
C++
// Exceptions-enabled variant of cleanup-conditional.cpp.
|
|
// When -fcxx-exceptions is active, cleanup scopes use "cleanup all" (both
|
|
// normal and exception paths) and the LLVM lowering emits invoke/landingpad
|
|
// instead of plain calls for operations that can throw.
|
|
//
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fcxx-exceptions -fexceptions %s -o %t.cir
|
|
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -fcxx-exceptions -fexceptions %s -o %t-cir.ll
|
|
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fcxx-exceptions -fexceptions %s -o %t.ll
|
|
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
|
|
|
|
struct S {
|
|
S();
|
|
~S();
|
|
int get();
|
|
};
|
|
|
|
struct A {
|
|
A();
|
|
~A();
|
|
int get();
|
|
};
|
|
|
|
struct B {
|
|
B();
|
|
~B();
|
|
int get();
|
|
};
|
|
|
|
void test_ternary_temporary(bool c, int x) {
|
|
int result = c ? S().get() : x;
|
|
}
|
|
// CIR-LABEL: @_Z22test_ternary_temporarybi
|
|
// CIR: %[[TMP:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["ref.tmp0"]
|
|
// CIR: %[[ACTIVE:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: %[[COND:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
|
|
// CIR: %[[FALSE:.*]] = cir.const #false
|
|
// CIR: cir.store %[[FALSE]], %[[ACTIVE]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %{{.*}} = cir.ternary(%[[COND]], true {
|
|
// CIR: cir.call @_ZN1SC1Ev(%[[TMP]])
|
|
// CIR: %[[TRUE:.*]] = cir.const #true
|
|
// CIR: cir.store %[[TRUE]], %[[ACTIVE]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[GET_RESULT:.*]] = cir.call @_ZN1S3getEv(%[[TMP]])
|
|
// CIR: cir.yield %[[GET_RESULT]] : !s32i
|
|
// CIR: }, false {
|
|
// CIR: cir.yield
|
|
// CIR: cir.yield
|
|
// With exceptions, the cleanup runs on both normal and EH paths.
|
|
// CIR: } cleanup all {
|
|
// CIR: %[[IS_ACTIVE:.*]] = cir.load {{.*}} %[[ACTIVE]]
|
|
// CIR: cir.if %[[IS_ACTIVE]] {
|
|
// CIR: cir.call @_ZN1SD1Ev(%[[TMP]])
|
|
// CIR: }
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
|
|
// LLVM-LABEL: define dso_local void @_Z22test_ternary_temporarybi(
|
|
// LLVM-SAME: personality ptr @__gxx_personality_v0
|
|
// LLVM: %[[TMP:.*]] = alloca %struct.S
|
|
// LLVM: %[[ACTIVE:.*]] = alloca i8
|
|
// LLVM: store i8 0, ptr %[[ACTIVE]]
|
|
// LLVM: br i1 %{{.*}}, label %[[TRUE_BR:.*]], label %[[FALSE_BR:.*]]
|
|
// Constructor and get() become invoke, unwinding to the landing pad.
|
|
// LLVM: [[TRUE_BR]]:
|
|
// LLVM: invoke void @_ZN1SC1Ev({{.*}} %[[TMP]])
|
|
// LLVM-NEXT: to label %[[CTOR_CONT:.*]] unwind label %[[PAD:.*]]
|
|
// LLVM: [[CTOR_CONT]]:
|
|
// LLVM: store i8 1, ptr %[[ACTIVE]]
|
|
// LLVM: %[[GET_RESULT:.*]] = invoke noundef i32 @_ZN1S3getEv({{.*}} %[[TMP]])
|
|
// LLVM-NEXT: to label %[[GET_CONT:.*]] unwind label %[[PAD]]
|
|
// LLVM: [[GET_CONT]]:
|
|
// LLVM: br label %[[MERGE:.*]]
|
|
// LLVM: [[FALSE_BR]]:
|
|
// LLVM: %[[XVAL:.*]] = load i32, ptr %{{.*}}
|
|
// LLVM: br label %[[MERGE]]
|
|
// LLVM: [[MERGE]]:
|
|
// LLVM: %[[PHI:.*]] = phi i32 [ %[[XVAL]], %[[FALSE_BR]] ], [ %[[GET_RESULT]], %[[GET_CONT]] ]
|
|
// Normal cleanup: check active flag, conditionally run destructor.
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTIVE]]
|
|
// LLVM: br i1 %{{.*}}, label %[[DTOR:.*]], label %[[SKIP_DTOR:.*]]
|
|
// LLVM: [[DTOR]]:
|
|
// LLVM: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// LLVM: br label %[[SKIP_DTOR]]
|
|
// EH cleanup: landingpad runs the same active-flag-guarded destructor.
|
|
// LLVM: [[PAD]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTIVE]]
|
|
// LLVM: br i1 %{{.*}}, label %[[EH_DTOR:.*]], label %[[EH_SKIP_DTOR:.*]]
|
|
// LLVM: [[EH_DTOR]]:
|
|
// LLVM: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// LLVM: br label %[[EH_SKIP_DTOR]]
|
|
// LLVM: [[EH_SKIP_DTOR]]:
|
|
// LLVM: resume { ptr, i32 }
|
|
|
|
// OGCG-LABEL: define dso_local void @_Z22test_ternary_temporarybi(
|
|
// OGCG-SAME: personality ptr @__gxx_personality_v0
|
|
// OGCG: entry:
|
|
// OGCG: store i1 false, ptr %[[ACTIVE:.*]]
|
|
// OGCG: br i1 %{{.*}}, label %[[TRUE_BR:.*]], label %[[FALSE_BR:.*]]
|
|
// The constructor is a plain call — no active cleanup exists yet.
|
|
// OGCG: [[TRUE_BR]]:
|
|
// OGCG: call void @_ZN1SC1Ev({{.*}} %[[TMP:.*]])
|
|
// OGCG: store i1 true, ptr %[[ACTIVE]]
|
|
// With the destructor now active, get() becomes invoke.
|
|
// OGCG: %[[GET_RESULT:.*]] = invoke {{.*}} i32 @_ZN1S3getEv({{.*}} %[[TMP]])
|
|
// OGCG-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
|
|
// OGCG: [[INVOKE_CONT]]:
|
|
// OGCG: br label %[[MERGE:.*]]
|
|
// OGCG: [[FALSE_BR]]:
|
|
// OGCG: br label %[[MERGE]]
|
|
// OGCG: [[MERGE]]:
|
|
// OGCG: %[[COND:.*]] = phi i32 [ %[[GET_RESULT]], %[[INVOKE_CONT]] ], [ %{{.*}}, %[[FALSE_BR]] ]
|
|
// Normal cleanup.
|
|
// OGCG: br i1 %{{.*}}, label %[[CLEANUP_ACT:.*]], label %[[CLEANUP_DONE:.*]]
|
|
// OGCG: [[CLEANUP_ACT]]:
|
|
// OGCG: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// OGCG: br label %[[CLEANUP_DONE]]
|
|
// OGCG: [[CLEANUP_DONE]]:
|
|
// OGCG: store i32 %[[COND]], ptr %{{.*}}
|
|
// EH cleanup: landing pad + same destructor check + resume.
|
|
// OGCG: [[LPAD]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: br i1 %{{.*}}, label %[[EH_DTOR:.*]], label %[[EH_AFTER_DTOR:.*]]
|
|
// OGCG: [[EH_DTOR]]:
|
|
// OGCG: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// OGCG: br label %[[EH_AFTER_DTOR]]
|
|
// OGCG: [[EH_AFTER_DTOR]]:
|
|
// OGCG: resume { ptr, i32 }
|
|
|
|
void test_ternary_both_branches(bool c) {
|
|
int result = c ? A().get() : B().get();
|
|
}
|
|
// CIR-LABEL: @_Z26test_ternary_both_branchesb
|
|
// CIR: %[[TMPA:.*]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["ref.tmp0"]
|
|
// CIR: %[[ACTA:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
|
|
// CIR: %[[TMPB:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
|
|
// CIR: %[[ACTB:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: %[[COND:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
|
|
// CIR: %[[FALSE_A:.*]] = cir.const #false
|
|
// CIR: cir.store %[[FALSE_A]], %[[ACTA]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[FALSE_B:.*]] = cir.const #false
|
|
// CIR: cir.store %[[FALSE_B]], %[[ACTB]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %{{.*}} = cir.ternary(%[[COND]], true {
|
|
// CIR: cir.call @_ZN1AC1Ev(%[[TMPA]])
|
|
// CIR: %[[TRUE_A:.*]] = cir.const #true
|
|
// CIR: cir.store %[[TRUE_A]], %[[ACTA]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[GET_A:.*]] = cir.call @_ZN1A3getEv(%[[TMPA]])
|
|
// CIR: cir.yield %[[GET_A]] : !s32i
|
|
// CIR: }, false {
|
|
// CIR: cir.call @_ZN1BC1Ev(%[[TMPB]])
|
|
// CIR: %[[TRUE_B:.*]] = cir.const #true
|
|
// CIR: cir.store %[[TRUE_B]], %[[ACTB]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[GET_B:.*]] = cir.call @_ZN1B3getEv(%[[TMPB]])
|
|
// CIR: cir.yield %[[GET_B]] : !s32i
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: %[[FLAG_B:.*]] = cir.load {{.*}} %[[ACTB]]
|
|
// CIR: cir.if %[[FLAG_B]] {
|
|
// CIR: cir.call @_ZN1BD1Ev(%[[TMPB]])
|
|
// CIR: }
|
|
// CIR: %[[FLAG_A:.*]] = cir.load {{.*}} %[[ACTA]]
|
|
// CIR: cir.if %[[FLAG_A]] {
|
|
// CIR: cir.call @_ZN1AD1Ev(%[[TMPA]])
|
|
// CIR: }
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
|
|
// LLVM-LABEL: define dso_local void @_Z26test_ternary_both_branchesb(
|
|
// LLVM-SAME: personality ptr @__gxx_personality_v0
|
|
// LLVM: %[[TMPA:.*]] = alloca %struct.A
|
|
// LLVM: %[[ACTA:.*]] = alloca i8
|
|
// LLVM: %[[TMPB:.*]] = alloca %struct.B
|
|
// LLVM: %[[ACTB:.*]] = alloca i8
|
|
// LLVM: store i8 0, ptr %[[ACTA]]
|
|
// LLVM: store i8 0, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[CONSTRUCT_A:.*]], label %[[CONSTRUCT_B:.*]]
|
|
// LLVM: [[CONSTRUCT_A]]:
|
|
// LLVM: invoke void @_ZN1AC1Ev({{.*}} %[[TMPA]])
|
|
// LLVM-NEXT: to label %[[A_CTOR_CONT:.*]] unwind label %[[PAD:.*]]
|
|
// LLVM: [[A_CTOR_CONT]]:
|
|
// LLVM: store i8 1, ptr %[[ACTA]], align 1
|
|
// LLVM: %[[CALLA:.*]] = invoke {{.*}} i32 @_ZN1A3getEv({{.*}} %[[TMPA]])
|
|
// LLVM-NEXT: to label %[[A_GET_CONT:.*]] unwind label %[[LPAD:.*]]
|
|
// LLVM: [[CONSTRUCT_B]]:
|
|
// LLVM: invoke void @_ZN1BC1Ev({{.*}} %[[TMPB]])
|
|
// LLVM-NEXT: to label %[[B_CTOR_CONT:.*]] unwind label %[[LPAD]]
|
|
// LLVM: [[B_CTOR_CONT]]:
|
|
// LLVM: store i8 1, ptr %[[ACTB]]
|
|
// LLVM: %[[CALLB:.*]] = invoke {{.*}} i32 @_ZN1B3getEv({{.*}} %[[TMPB]])
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[PAD]]
|
|
// Normal cleanup: check both active flags.
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[DTOR_B:.*]], label %[[SKIP_DTOR_B:.*]]
|
|
// LLVM: [[DTOR_B]]:
|
|
// LLVM: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// LLVM: [[SKIP_DTOR_B]]:
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTA]]
|
|
// LLVM: br i1 %{{.*}}, label %[[DTOR_A:.*]], label %[[SKIP_DTOR_A:.*]]
|
|
// LLVM: [[DTOR_A]]:
|
|
// LLVM: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// EH cleanup: single landingpad, then same active-flag checks for both.
|
|
// LLVM: [[LPAD]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[EH_DTOR_B:.*]], label %[[EH_SKIP_DTOR_B:.*]]
|
|
// LLVM: [[EH_DTOR_B]]:
|
|
// LLVM: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// LLVM: [[EH_SKIP_DTOR_B]]:
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTA]]
|
|
// LLVM: br i1 %{{.*}}, label %[[EH_DTOR_A:.*]], label %[[EH_SKIP_DTOR_A:.*]]
|
|
// LLVM: [[EH_DTOR_A]]:
|
|
// LLVM: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// LLVM: [[EH_SKIP_DTOR_A]]:
|
|
// LLVM: resume { ptr, i32 }
|
|
|
|
// OGCG-LABEL: define dso_local void @_Z26test_ternary_both_branchesb(
|
|
// OGCG-SAME: personality ptr @__gxx_personality_v0
|
|
// OGCG: entry:
|
|
// OGCG: store i1 false, ptr %[[ACTA:.*]]
|
|
// OGCG: store i1 false, ptr %[[ACTB:.*]]
|
|
// OGCG: br i1 %{{.*}}, label %[[TRUE_BR:.*]], label %[[FALSE_BR:.*]]
|
|
// A constructor is call (no active cleanup yet); A get() is invoke.
|
|
// OGCG: [[TRUE_BR]]:
|
|
// OGCG: call void @_ZN1AC1Ev({{.*}} %[[TMPA:.*]])
|
|
// OGCG: store i1 true, ptr %[[ACTA]]
|
|
// OGCG: %[[CALLA:.*]] = invoke {{.*}} i32 @_ZN1A3getEv({{.*}} %[[TMPA]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD1:.*]]
|
|
// B constructor is invoke (A cleanup is active); B get() invokes to a second pad.
|
|
// OGCG: [[FALSE_BR]]:
|
|
// OGCG: invoke void @_ZN1BC1Ev({{.*}} %[[TMPB:.*]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD1]]
|
|
// OGCG: store i1 true, ptr %[[ACTB]]
|
|
// OGCG: invoke {{.*}} i32 @_ZN1B3getEv({{.*}} %[[TMPB]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD2:.*]]
|
|
// Normal cleanup: B first, then A (reverse construction order).
|
|
// OGCG: [[MERGE:.*]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[DTOR_B:.*]], label %[[AFTER_DTOR_B:.*]]
|
|
// OGCG: [[DTOR_B]]:
|
|
// OGCG: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// OGCG: br label %[[AFTER_DTOR_B]]
|
|
// OGCG: [[AFTER_DTOR_B]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[DTOR_A:.*]], label %[[AFTER_DTOR_A:.*]]
|
|
// OGCG: [[DTOR_A]]:
|
|
// OGCG: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// OGCG: br label %[[AFTER_DTOR_A]]
|
|
// First landing pad: from A.get() or B ctor — only A cleanup needed.
|
|
// OGCG: [[LPAD1]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: br label %[[EH_CLEANUP:.*]]
|
|
// Second landing pad: from B.get() — B cleanup, then A cleanup.
|
|
// OGCG: [[LPAD2]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: br i1 %{{.*}}, label %[[EH_DTOR_B:.*]], label %[[EH_AFTER_DTOR_B:.*]]
|
|
// OGCG: [[EH_DTOR_B]]:
|
|
// OGCG: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// OGCG: br label %[[EH_AFTER_DTOR_B]]
|
|
// OGCG: [[EH_AFTER_DTOR_B]]:
|
|
// OGCG: br label %[[EH_CLEANUP]]
|
|
// Shared EH cleanup for A's destructor, then resume.
|
|
// OGCG: [[EH_CLEANUP]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[EH_DTOR_A:.*]], label %[[EH_AFTER_DTOR_A:.*]]
|
|
// OGCG: [[EH_DTOR_A]]:
|
|
// OGCG: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// OGCG: br label %[[EH_AFTER_DTOR_A]]
|
|
// OGCG: [[EH_AFTER_DTOR_A]]:
|
|
// OGCG: resume { ptr, i32 }
|
|
|
|
int test_return_ternary(bool c) {
|
|
return c ? A().get() : B().get();
|
|
}
|
|
// CIR-LABEL: @_Z19test_return_ternaryb
|
|
// CIR: %[[TMPA:.*]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["ref.tmp0"]
|
|
// CIR: %[[ACTA:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
|
|
// CIR: %[[TMPB:.*]] = cir.alloca !rec_B, !cir.ptr<!rec_B>, ["ref.tmp1"]
|
|
// CIR: %[[ACTB:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cleanup.cond"]
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: %[[COND:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
|
|
// CIR: %[[FALSE_A:.*]] = cir.const #false
|
|
// CIR: cir.store %[[FALSE_A]], %[[ACTA]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[FALSE_B:.*]] = cir.const #false
|
|
// CIR: cir.store %[[FALSE_B]], %[[ACTB]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %{{.*}} = cir.ternary(%[[COND]], true {
|
|
// CIR: cir.call @_ZN1AC1Ev(%[[TMPA]])
|
|
// CIR: %[[TRUE_A:.*]] = cir.const #true
|
|
// CIR: cir.store %[[TRUE_A]], %[[ACTA]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[GET_A:.*]] = cir.call @_ZN1A3getEv(%[[TMPA]])
|
|
// CIR: cir.yield %[[GET_A]] : !s32i
|
|
// CIR: }, false {
|
|
// CIR: cir.call @_ZN1BC1Ev(%[[TMPB]])
|
|
// CIR: %[[TRUE_B:.*]] = cir.const #true
|
|
// CIR: cir.store %[[TRUE_B]], %[[ACTB]] : !cir.bool, !cir.ptr<!cir.bool>
|
|
// CIR: %[[GET_B:.*]] = cir.call @_ZN1B3getEv(%[[TMPB]])
|
|
// CIR: cir.yield %[[GET_B]] : !s32i
|
|
// CIR: })
|
|
// CIR: cir.store %{{.*}}, %{{.*}} : !s32i, !cir.ptr<!s32i>
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: %[[FLAG_B:.*]] = cir.load {{.*}} %[[ACTB]]
|
|
// CIR: cir.if %[[FLAG_B]] {
|
|
// CIR: cir.call @_ZN1BD1Ev(%[[TMPB]])
|
|
// CIR: }
|
|
// CIR: %[[FLAG_A:.*]] = cir.load {{.*}} %[[ACTA]]
|
|
// CIR: cir.if %[[FLAG_A]] {
|
|
// CIR: cir.call @_ZN1AD1Ev(%[[TMPA]])
|
|
// CIR: }
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
// CIR: %[[RET:.*]] = cir.load %{{.*}} : !cir.ptr<!s32i>, !s32i
|
|
// CIR: cir.return %[[RET]] : !s32i
|
|
|
|
// LLVM-LABEL: define dso_local noundef i32 @_Z19test_return_ternaryb(
|
|
// LLVM-SAME: personality ptr @__gxx_personality_v0
|
|
// LLVM: %[[RETVAL:.*]] = alloca i32
|
|
// LLVM: %[[TMPA:.*]] = alloca %struct.A
|
|
// LLVM: %[[ACTA:.*]] = alloca i8
|
|
// LLVM: %[[TMPB:.*]] = alloca %struct.B
|
|
// LLVM: %[[ACTB:.*]] = alloca i8
|
|
// LLVM: store i8 0, ptr %[[ACTA]]
|
|
// LLVM: store i8 0, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[CONSTRUCT_A:.*]], label %[[CONSTRUCT_B:.*]]
|
|
// LLVM: [[CONSTRUCT_A]]:
|
|
// LLVM: invoke void @_ZN1AC1Ev({{.*}} %[[TMPA]])
|
|
// LLVM-NEXT: to label %[[A_CTOR_CONT:.*]] unwind label %[[LPAD:.*]]
|
|
// LLVM: [[A_CTOR_CONT]]:
|
|
// LLVM: store i8 1, ptr %[[ACTA]], align 1
|
|
// LLVM: %[[CALLA:.*]] = invoke {{.*}} i32 @_ZN1A3getEv({{.*}} %[[TMPA]])
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[LPAD]]
|
|
// LLVM: [[CONSTRUCT_B]]:
|
|
// LLVM: invoke void @_ZN1BC1Ev({{.*}} %[[TMPB]])
|
|
// LLVM-NEXT: to label %[[B_CTOR_CONT:.*]] unwind label %[[LPAD]]
|
|
// LLVM: [[B_CTOR_CONT]]:
|
|
// LLVM: store i8 1, ptr %[[ACTB]], align 1
|
|
// LLVM: %[[CALLB:.*]] = invoke {{.*}} i32 @_ZN1B3getEv({{.*}} %[[TMPB]])
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[LPAD]]
|
|
// LLVM: store i32 %{{.*}}, ptr %[[RETVAL]]
|
|
// Normal cleanup: check both active flags.
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[DTOR_B:.*]], label %[[SKIP_DTOR_B:.*]]
|
|
// LLVM: [[DTOR_B]]:
|
|
// LLVM: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// LLVM: [[SKIP_DTOR_B]]:
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTA]]
|
|
// LLVM: br i1 %{{.*}}, label %[[DTOR_A:.*]], label %[[SKIP_DTOR_A:.*]]
|
|
// LLVM: [[DTOR_A]]:
|
|
// LLVM: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// EH cleanup: same active-flag checks, then resume.
|
|
// LLVM: [[LPAD]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTB]]
|
|
// LLVM: br i1 %{{.*}}, label %[[EH_DTOR_B:.*]], label %[[EH_SKIP_DTOR_B:.*]]
|
|
// LLVM: [[EH_DTOR_B]]:
|
|
// LLVM: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// LLVM: [[EH_SKIP_DTOR_B]]:
|
|
// LLVM: %{{.*}} = load i8, ptr %[[ACTA]]
|
|
// LLVM: br i1 %{{.*}}, label %[[EH_DTOR_A:.*]], label %[[EH_SKIP_DTOR_A:.*]]
|
|
// LLVM: [[EH_DTOR_A]]:
|
|
// LLVM: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// LLVM: [[EH_SKIP_DTOR_A]]:
|
|
// LLVM: resume { ptr, i32 }
|
|
// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]]
|
|
// LLVM: ret i32 %[[RET]]
|
|
|
|
// OGCG-LABEL: define dso_local noundef i32 @_Z19test_return_ternaryb(
|
|
// OGCG-SAME: personality ptr @__gxx_personality_v0
|
|
// OGCG: entry:
|
|
// OGCG: store i1 false, ptr %[[ACTA:.*]]
|
|
// OGCG: store i1 false, ptr %[[ACTB:.*]]
|
|
// OGCG: br i1 %{{.*}}, label %[[TRUE_BR:.*]], label %[[FALSE_BR:.*]]
|
|
// OGCG: [[TRUE_BR]]:
|
|
// OGCG: call void @_ZN1AC1Ev({{.*}} %[[TMPA:.*]])
|
|
// OGCG: store i1 true, ptr %[[ACTA]]
|
|
// OGCG: %[[CALLA:.*]] = invoke {{.*}} i32 @_ZN1A3getEv({{.*}} %[[TMPA]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD1:.*]]
|
|
// OGCG: [[FALSE_BR]]:
|
|
// OGCG: invoke void @_ZN1BC1Ev({{.*}} %[[TMPB:.*]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD1]]
|
|
// OGCG: store i1 true, ptr %[[ACTB]]
|
|
// OGCG: invoke {{.*}} i32 @_ZN1B3getEv({{.*}} %[[TMPB]])
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD2:.*]]
|
|
// Normal cleanup: B first, then A.
|
|
// OGCG: [[MERGE:.*]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[DTOR_B:.*]], label %[[AFTER_DTOR_B:.*]]
|
|
// OGCG: [[DTOR_B]]:
|
|
// OGCG: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// OGCG: br label %[[AFTER_DTOR_B]]
|
|
// OGCG: [[AFTER_DTOR_B]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[DTOR_A:.*]], label %[[AFTER_DTOR_A:.*]]
|
|
// OGCG: [[DTOR_A]]:
|
|
// OGCG: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// OGCG: br label %[[AFTER_DTOR_A]]
|
|
// OGCG: [[AFTER_DTOR_A]]:
|
|
// OGCG: ret i32 %{{.*}}
|
|
// First landing pad: from A.get() or B ctor.
|
|
// OGCG: [[LPAD1]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: br label %[[EH_CLEANUP:.*]]
|
|
// Second landing pad: from B.get() — B cleanup, then A cleanup.
|
|
// OGCG: [[LPAD2]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: br i1 %{{.*}}, label %[[EH_DTOR_B:.*]], label %[[EH_AFTER_DTOR_B:.*]]
|
|
// OGCG: [[EH_DTOR_B]]:
|
|
// OGCG: call void @_ZN1BD1Ev({{.*}} %[[TMPB]])
|
|
// OGCG: br label %[[EH_AFTER_DTOR_B]]
|
|
// OGCG: [[EH_AFTER_DTOR_B]]:
|
|
// OGCG: br label %[[EH_CLEANUP]]
|
|
// OGCG: [[EH_CLEANUP]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[EH_DTOR_A:.*]], label %[[EH_AFTER_DTOR_A:.*]]
|
|
// OGCG: [[EH_DTOR_A]]:
|
|
// OGCG: call void @_ZN1AD1Ev({{.*}} %[[TMPA]])
|
|
// OGCG: br label %[[EH_AFTER_DTOR_A]]
|
|
// OGCG: [[EH_AFTER_DTOR_A]]:
|
|
// OGCG: resume { ptr, i32 }
|
|
|
|
// False positive: ExprWithCleanups wraps a ternary, but S() is constructed
|
|
// outside the conditional so no cleanup is deferred. The cleanup.scope still
|
|
// uses "cleanup all" for the unconditional destructor.
|
|
int test_false_positive_conditional(bool c) {
|
|
return S().get() ? 1 : 2;
|
|
}
|
|
// CIR-LABEL: @_Z31test_false_positive_conditionalb
|
|
// CIR-NOT: cir.alloca {{.*}} ["cleanup.cond"]
|
|
// CIR: %[[TMP:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["ref.tmp0"]
|
|
// CIR: cir.call @_ZN1SC1Ev(%[[TMP]])
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: %[[VAL:.*]] = cir.call @_ZN1S3getEv(%[[TMP]])
|
|
// CIR: %[[BOOL:.*]] = cir.cast int_to_bool %[[VAL]]
|
|
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
|
|
// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i
|
|
// CIR: %[[SEL:.*]] = cir.select if %[[BOOL]] then %[[ONE]] else %[[TWO]]
|
|
// CIR: cir.store %[[SEL]], %{{.*}} : !s32i, !cir.ptr<!s32i>
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: cir.call @_ZN1SD1Ev(%[[TMP]])
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
|
|
// LLVM-LABEL: define dso_local noundef i32 @_Z31test_false_positive_conditionalb(
|
|
// LLVM-SAME: personality ptr @__gxx_personality_v0
|
|
// LLVM: %[[RETVAL:.*]] = alloca i32
|
|
// LLVM: %[[TMP:.*]] = alloca %struct.S
|
|
// The constructor is call — no active EH cleanup yet.
|
|
// LLVM: call void @_ZN1SC1Ev({{.*}} %[[TMP]])
|
|
// get() becomes invoke because the destructor cleanup is active.
|
|
// LLVM: %[[VAL:.*]] = invoke {{.*}} i32 @_ZN1S3getEv({{.*}} %[[TMP]])
|
|
// LLVM-NEXT: to label %[[GET_CONT:.*]] unwind label %[[LPAD:.*]]
|
|
// LLVM: [[GET_CONT]]:
|
|
// LLVM: %[[CMP:.*]] = icmp ne i32 %[[VAL]], 0
|
|
// LLVM: %[[SEL:.*]] = select i1 %[[CMP]], i32 1, i32 2
|
|
// LLVM: store i32 %[[SEL]], ptr %[[RETVAL]]
|
|
// Normal path: unconditional destructor.
|
|
// LLVM: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// EH path: landingpad + unconditional destructor + resume.
|
|
// LLVM: [[LPAD]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// LLVM: resume { ptr, i32 }
|
|
// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]]
|
|
// LLVM: ret i32 %[[RET]]
|
|
|
|
// OGCG-LABEL: define dso_local noundef i32 @_Z31test_false_positive_conditionalb(
|
|
// OGCG-SAME: personality ptr @__gxx_personality_v0
|
|
// OGCG: entry:
|
|
// The constructor is call; get() is invoke.
|
|
// OGCG: call void @_ZN1SC1Ev({{.*}} %[[TMP:.*]])
|
|
// OGCG: %[[VAL:.*]] = invoke {{.*}} i32 @_ZN1S3getEv({{.*}} %[[TMP]])
|
|
// OGCG-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
|
|
// OGCG: [[INVOKE_CONT]]:
|
|
// OGCG: %[[CMP:.*]] = icmp ne i32 %[[VAL]], 0
|
|
// OGCG: %[[SEL:.*]] = select i1 %[[CMP]], i32 1, i32 2
|
|
// Normal path: unconditional destructor + return.
|
|
// OGCG: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// OGCG: ret i32 %[[SEL]]
|
|
// EH path: unconditional destructor + resume.
|
|
// OGCG: [[LPAD]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: call void @_ZN1SD1Ev({{.*}} %[[TMP]])
|
|
// OGCG: resume { ptr, i32 }
|
|
|
|
// Test nested ExprWithCleanups nodes, each containing a ternary operator.
|
|
//
|
|
// The outer ExprWithCleanups wraps the full-expression
|
|
// `S result = ({...}) ? (...) : S(5);`
|
|
// The inner ExprWithCleanups wraps the variable initializer
|
|
// `S s = c1 ? S(1) : S(2);`
|
|
// inside the statement expression, which is its own full-expression context.
|
|
//
|
|
// Both contain ConditionalOperators — exercising the save/restore of
|
|
// fullExprCleanupScope state.
|
|
|
|
struct T {
|
|
T();
|
|
T(int);
|
|
T(const T &);
|
|
~T();
|
|
operator bool();
|
|
};
|
|
|
|
void test_nested_ewc(bool c1, bool c2) {
|
|
T result = ({ T s = c1 ? T(1) : T(2); s; }) ? (c2 ? T(3) : T(4))
|
|
: T(5);
|
|
}
|
|
|
|
// CIR-LABEL: @_Z15test_nested_ewcbb
|
|
// CIR: %[[RESULT:.*]] = cir.alloca !rec_T, !cir.ptr<!rec_T>, ["result", init]
|
|
// CIR: %[[REF_TMP:.*]] = cir.alloca !rec_T, !cir.ptr<!rec_T>, ["ref.tmp0"]
|
|
// Inner cir.scope for the statement expression.
|
|
// CIR: cir.scope {
|
|
// CIR: %[[S:.*]] = cir.alloca !rec_T, !cir.ptr<!rec_T>, ["s", init]
|
|
// Inner ternary: c1 ? T(1) : T(2) — no cleanup scope needed.
|
|
// CIR: %[[C1:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
|
|
// CIR: cir.if %[[C1]] {
|
|
// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
|
|
// CIR: cir.call @_ZN1TC1Ei(%[[S]], %[[ONE]])
|
|
// CIR: } else {
|
|
// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i
|
|
// CIR: cir.call @_ZN1TC1Ei(%[[S]], %[[TWO]])
|
|
// CIR: }
|
|
// Copy s into ref.tmp; destroy s on all paths (normal + EH).
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: cir.call @_ZN1TC1ERKS_(%[[REF_TMP]], %[[S]])
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: cir.call @_ZN1TD1Ev(%[[S]])
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
// CIR: }
|
|
// Outer cleanup scope: operator bool() + outer ternary + destroys ref.tmp.
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: %[[BOOL:.*]] = cir.call @_ZN1TcvbEv(%[[REF_TMP]])
|
|
// CIR: cir.if %[[BOOL]] {
|
|
// CIR: %[[C2:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
|
|
// CIR: cir.if %[[C2]] {
|
|
// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
|
|
// CIR: cir.call @_ZN1TC1Ei(%[[RESULT]], %[[THREE]])
|
|
// CIR: } else {
|
|
// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !s32i
|
|
// CIR: cir.call @_ZN1TC1Ei(%[[RESULT]], %[[FOUR]])
|
|
// CIR: }
|
|
// CIR: } else {
|
|
// CIR: %[[FIVE:.*]] = cir.const #cir.int<5> : !s32i
|
|
// CIR: cir.call @_ZN1TC1Ei(%[[RESULT]], %[[FIVE]])
|
|
// CIR: }
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: cir.call @_ZN1TD1Ev(%[[REF_TMP]])
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
// result destructor.
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: cir.yield
|
|
// CIR: } cleanup all {
|
|
// CIR: cir.call @_ZN1TD1Ev(%[[RESULT]])
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
|
|
// LLVM-LABEL: define dso_local void @_Z15test_nested_ewcbb(
|
|
// LLVM-SAME: personality ptr @__gxx_personality_v0
|
|
// Inner ternary: c1 ? T(1) : T(2) — plain calls (no active cleanup yet).
|
|
// LLVM: br i1 %{{.*}}, label %[[T1:.*]], label %[[T2:.*]]
|
|
// LLVM: [[T1]]:
|
|
// LLVM: call void @_ZN1TC1Ei({{.*}} %[[S:.*]], i32 {{.*}} 1)
|
|
// LLVM: br label %[[INNER_MERGE:.*]]
|
|
// LLVM: [[T2]]:
|
|
// LLVM: call void @_ZN1TC1Ei({{.*}} %[[S]], i32 {{.*}} 2)
|
|
// LLVM: br label %[[INNER_MERGE]]
|
|
// Copy construct: invoke (s destructor is active EH cleanup).
|
|
// LLVM: [[INNER_MERGE]]:
|
|
// LLVM: invoke void @_ZN1TC1ERKS_({{.*}} %[[REF_TMP:.*]], {{.*}} %[[S]])
|
|
// LLVM-NEXT: to label %[[COPY_CONT:.*]] unwind label %[[LPAD_S:.*]]
|
|
// LLVM: [[COPY_CONT]]:
|
|
// LLVM: call void @_ZN1TD1Ev({{.*}} %[[S]])
|
|
// EH cleanup for s: appears here in block order, before outer ternary.
|
|
// LLVM: [[LPAD_S]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: call void @_ZN1TD1Ev({{.*}} %[[S]])
|
|
// LLVM: resume { ptr, i32 }
|
|
// Outer ternary: operator bool() is invoke (ref.tmp cleanup is active).
|
|
// LLVM: %[[BOOL:.*]] = invoke {{.*}} i1 @_ZN1TcvbEv({{.*}} %[[REF_TMP]])
|
|
// LLVM-NEXT: to label %[[BOOL_CONT:.*]] unwind label %[[LPAD_REF:.*]]
|
|
// LLVM: [[BOOL_CONT]]:
|
|
// LLVM: br i1 %[[BOOL]], label %[[TRUE:.*]], label %[[FALSE:.*]]
|
|
// LLVM: [[TRUE]]:
|
|
// LLVM: br i1 %{{.*}}, label %[[T3:.*]], label %[[T4:.*]]
|
|
// LLVM: [[T3]]:
|
|
// LLVM: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT:.*]], i32 {{.*}} 3)
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// LLVM: [[T4]]:
|
|
// LLVM: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT]], i32 {{.*}} 4)
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// LLVM: [[FALSE]]:
|
|
// LLVM: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT]], i32 {{.*}} 5)
|
|
// LLVM-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// Normal cleanup: destroy ref.tmp.
|
|
// LLVM: call void @_ZN1TD1Ev({{.*}} %[[REF_TMP]])
|
|
// EH cleanup for ref.tmp: landingpad + destroy ref.tmp + resume.
|
|
// LLVM: [[LPAD_REF]]:
|
|
// LLVM: landingpad { ptr, i32 }
|
|
// LLVM-NEXT: cleanup
|
|
// LLVM: call void @_ZN1TD1Ev({{.*}} %[[REF_TMP]])
|
|
// LLVM: resume { ptr, i32 }
|
|
// Result destructor on normal path.
|
|
// LLVM: call void @_ZN1TD1Ev({{.*}} %[[RESULT]])
|
|
|
|
// OGCG-LABEL: define dso_local void @_Z15test_nested_ewcbb(
|
|
// OGCG-SAME: personality ptr @__gxx_personality_v0
|
|
// Inner ternary: c1 ? T(1) : T(2).
|
|
// OGCG: br i1 %{{.*}}, label %[[T1:.*]], label %[[T2:.*]]
|
|
// OGCG: [[T1]]:
|
|
// OGCG: call void @_ZN1TC1Ei({{.*}} %[[S:.*]], i32 {{.*}} 1)
|
|
// OGCG: br label %[[INNER_MERGE:.*]]
|
|
// OGCG: [[T2]]:
|
|
// OGCG: call void @_ZN1TC1Ei({{.*}} %[[S]], i32 {{.*}} 2)
|
|
// OGCG: br label %[[INNER_MERGE]]
|
|
// Copy construct ref.tmp: invoke (s destructor is EH cleanup).
|
|
// OGCG: [[INNER_MERGE]]:
|
|
// OGCG: invoke void @_ZN1TC1ERKS_({{.*}} %[[REF_TMP:.*]], {{.*}} %[[S]])
|
|
// OGCG-NEXT: to label %[[COPY_CONT:.*]] unwind label %[[LPAD_S:.*]]
|
|
// OGCG: [[COPY_CONT]]:
|
|
// OGCG: call void @_ZN1TD1Ev({{.*}} %[[S]])
|
|
// Outer: operator bool() is invoke, then outer ternary branches.
|
|
// OGCG: %[[BOOL:.*]] = invoke {{.*}} i1 @_ZN1TcvbEv({{.*}} %[[REF_TMP]])
|
|
// OGCG-NEXT: to label %[[BOOL_CONT:.*]] unwind label %[[LPAD_REF:.*]]
|
|
// OGCG: [[BOOL_CONT]]:
|
|
// OGCG: br i1 %[[BOOL]], label %[[TRUE:.*]], label %[[FALSE:.*]]
|
|
// OGCG: [[TRUE]]:
|
|
// OGCG: br i1 %{{.*}}, label %[[T3:.*]], label %[[T4:.*]]
|
|
// OGCG: [[T3]]:
|
|
// OGCG: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT:.*]], i32 {{.*}} 3)
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// OGCG: [[T4]]:
|
|
// OGCG: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT]], i32 {{.*}} 4)
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// OGCG: [[FALSE]]:
|
|
// OGCG: invoke void @_ZN1TC1Ei({{.*}} %[[RESULT]], i32 {{.*}} 5)
|
|
// OGCG-NEXT: to label %{{.*}} unwind label %[[LPAD_REF]]
|
|
// Normal cleanup: destroy ref.tmp, then result.
|
|
// OGCG: call void @_ZN1TD1Ev({{.*}} %[[REF_TMP]])
|
|
// OGCG: call void @_ZN1TD1Ev({{.*}} %[[RESULT]])
|
|
// EH cleanup for s: landingpad + destroy s.
|
|
// OGCG: [[LPAD_S]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: call void @_ZN1TD1Ev({{.*}} %[[S]])
|
|
// EH cleanup for ref.tmp: landingpad + destroy ref.tmp.
|
|
// OGCG: [[LPAD_REF]]:
|
|
// OGCG: landingpad { ptr, i32 }
|
|
// OGCG-NEXT: cleanup
|
|
// OGCG: call void @_ZN1TD1Ev({{.*}} %[[REF_TMP]])
|
|
// OGCG: resume { ptr, i32 }
|