blob: 79c614beb5a11334a8d5b71a69c5b4b9bd40aeae [file] [edit]
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %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 %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 %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
struct Base { ~Base(); };
namespace std {
template<typename T>
struct unique_ptr {
unique_ptr(T*);
~unique_ptr();
};
};
struct Wrapper {
std::unique_ptr<Base> ptr;
Wrapper();
explicit Wrapper(std::unique_ptr<Base> p);
static Wrapper empty();
};
bool flag;
Base* getSource();
// The use of unique_ptr here forces the creation of a temporary aggregate
// in the true branch of the conditional, which must be conditionally destroyed
// in the cleanup. Without the use of unique_ptr, the object returned by
// getSource would be passed directly to Wrapper, which uses a function-level
// alloca.
//
// The temporary aggregate must be hoisted out of the cleanup scope in order
// to properly dominate the cleanup region.
Wrapper makeWrapper() {
return flag
? Wrapper(std::unique_ptr<Base>(getSource()))
: Wrapper::empty();
}
// CIR: cir.func {{.*}} @_Z11makeWrapperv() -> !rec_Wrapper
// CIR: %[[RETVAL:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!rec_Wrapper>
// CIR: %[[CLEANUP_COND:.*]] = cir.alloca "cleanup.cond" {{.*}} : !cir.ptr<!cir.bool>
// CIR: %[[AGG_TMP0:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_std3A3Aunique_ptr3CBase3E>
// CIR: cir.cleanup.scope {
// CIR: %[[FLAG:.*]] = cir.load{{.*}} %{{.*}}
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store %[[FALSE]], %[[CLEANUP_COND]]
// CIR: cir.if %[[FLAG]] {
// CIR: %[[SOURCE:.*]] = cir.call @_Z9getSourcev()
// CIR: cir.call @_ZNSt10unique_ptrI4BaseEC1EPS0_(%[[AGG_TMP0]], %[[SOURCE]])
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
// CIR: %[[AGG_TMP0_LOAD:.*]] = cir.load{{.*}} %[[AGG_TMP0]]
// CIR: cir.call @_ZN7WrapperC1ESt10unique_ptrI4BaseE(%[[RETVAL]], %[[AGG_TMP0_LOAD]])
// CIR: } else {
// CIR: %[[EMPTY:.*]] = cir.call @_ZN7Wrapper5emptyEv()
// CIR: cir.store{{.*}} %[[EMPTY]], %[[RETVAL]] : !rec_Wrapper, !cir.ptr<!rec_Wrapper>
// CIR: }
// CIR: cir.yield
// CIR: } cleanup normal {
// CIR: %[[SHOULD_CLEANUP:.*]] = cir.load{{.*}} %[[CLEANUP_COND]]
// CIR: cir.if %[[SHOULD_CLEANUP]] {
// CIR: cir.call @_ZNSt10unique_ptrI4BaseED1Ev(%[[AGG_TMP0]])
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: %[[RET:.*]] = cir.load %[[RETVAL]]
// CIR: cir.return %[[RET]] : !rec_Wrapper
// LLVM: define {{.*}} %struct.Wrapper @_Z11makeWrapperv()
// LLVM: %[[RETVAL:.*]] = alloca %struct.Wrapper
// LLVM: %[[CLEANUP_COND:.*]] = alloca i8
// LLVM: %[[AGG_TMP0:.*]] = alloca %"struct.std::unique_ptr<Base>"
// LLVM: br label %[[INIT:.*]]
// LLVM: [[INIT]]:
// LLVM: br i1 %{{.*}}, label %[[CONSTRUCT_TRUE:.*]], label %[[CONSTRUCT_FALSE:.*]]
// LLVM: [[CONSTRUCT_TRUE]]:
// LLVM: %[[SOURCE:.*]] = call {{.*}} ptr @_Z9getSourcev()
// LLVM: call void @_ZNSt10unique_ptrI4BaseEC1EPS0_(ptr {{.*}} %[[AGG_TMP0]], ptr {{.*}} %[[SOURCE]])
// LLVM: store i8 1, ptr %[[CLEANUP_COND]]
// LLVM: %[[AGG_TMP0_LOAD:.*]] = load %"struct.std::unique_ptr<Base>", ptr %[[AGG_TMP0]]
// LLVM: call void @_ZN7WrapperC1ESt10unique_ptrI4BaseE(ptr {{.*}} %[[RETVAL]], %"struct.std::unique_ptr<Base>" %[[AGG_TMP0_LOAD]])
// LLVM: br label %[[CONSTRUCT_CONTINUE:.*]]
// LLVM: [[CONSTRUCT_FALSE]]:
// LLVM: %[[EMPTY:.*]] = call %struct.Wrapper @_ZN7Wrapper5emptyEv()
// LLVM: store %struct.Wrapper %[[EMPTY]], ptr %[[RETVAL]]
// LLVM: br label %[[CONSTRUCT_DONE:.*]]
// LLVM: [[CONSTRUCT_DONE]]:
// LLVM: %[[CLEANUP_FLAG:.*]] = load i8, ptr %[[CLEANUP_COND]]
// LLVM: %[[SHOULD_CLEANUP:.*]] = trunc i8 %[[CLEANUP_FLAG]] to i1
// LLVM: br i1 %[[SHOULD_CLEANUP]], label %[[NORMAL_CLEANUP:.*]], label %[[DONE:.*]]
// LLVM: [[NORMAL_CLEANUP]]:
// LLVM: call void @_ZNSt10unique_ptrI4BaseED1Ev(ptr {{.*}} %[[AGG_TMP0]])
// LLVM: br label %[[DONE:.*]]
// LLVM: [[DONE]]:
// LLVM: %[[RET:.*]] = load %struct.Wrapper, ptr %[[RETVAL]]
// LLVM: ret %struct.Wrapper %[[RET]]
// OGCG: define {{.*}} void @_Z11makeWrapperv(ptr{{.*}} sret(%struct.Wrapper) {{.*}} %[[RETVAL:.*]])
// OGCG: %[[RESULT_PTR:.*]] = alloca ptr
// OGCG: %[[AGG_TMP:.*]] = alloca %"struct.std::unique_ptr"
// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
// OGCG: store ptr %[[RETVAL]], ptr %[[RESULT_PTR]]
// OGCG: %[[FLAG:.*]] = load i8, ptr @flag, align 1
// OGCG: %[[LOADEDV:.*]] = icmp ne i8 %[[FLAG]], 0
// OGCG: store i1 false, ptr %[[CLEANUP_COND]]
// OGCG: br i1 %[[LOADEDV]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// OGCG: [[COND_TRUE]]:
// OGCG: %[[SOURCE:.*]] = call {{.*}} ptr @_Z9getSourcev()
// OGCG: call void @_ZNSt10unique_ptrI4BaseEC1EPS0_(ptr {{.*}} %[[AGG_TMP]], {{.*}} %[[SOURCE]])
// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
// OGCG: call void @_ZN7WrapperC1ESt10unique_ptrI4BaseE(ptr {{.*}} %[[RETVAL]], ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[COND_END:.*]]
// OGCG: [[COND_FALSE]]:
// OGCG: call void @_ZN7Wrapper5emptyEv(ptr {{.*}} %[[RETVAL]])
// OGCG: br label %[[COND_END:.*]]
// OGCG: [[COND_END]]:
// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[DONE:.*]]
// OGCG: [[CLEANUP_ACTION]]:
// OGCG: call void @_ZNSt10unique_ptrI4BaseED1Ev(ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[DONE]]
// OGCG: [[DONE]]:
// OGCG: ret void
struct APInt {
~APInt();
APInt uadd_sat();
};
struct APFixedPoint {
void add(int x) const;
};
// A conditional expression whose two arms both materialize into the same
// aggregate temporary pushes two deferred conditional cleanups that share
// the same underlying alloca.
void APFixedPoint::add(int x) const {
APInt ThisVal;
if (x)
x ? ThisVal : ThisVal.uadd_sat();
}
// CIR: cir.func {{.*}} @_ZNK12APFixedPoint3addEi(%{{.*}}: !cir.ptr<!rec_APFixedPoint>{{.*}}, %{{.*}}: !s32i{{.*}})
// CIR: %[[X_ADDR:.*]] = cir.alloca "x" {{.*}} init : !cir.ptr<!s32i>
// CIR: %[[THISVAL:.*]] = cir.alloca "ThisVal" {{.*}} : !cir.ptr<!rec_APInt>
// CIR: %[[CLEANUP_COND_TRUE:.*]] = cir.alloca "cleanup.cond" {{.*}} : !cir.ptr<!cir.bool>
// CIR: %[[CLEANUP_COND_FALSE:.*]] = cir.alloca "cleanup.cond" {{.*}} : !cir.ptr<!cir.bool>
// CIR: cir.cleanup.scope {
// CIR: cir.scope {
// CIR: %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]]
// CIR: %[[X_BOOL:.*]] = cir.cast int_to_bool %[[X]]
// CIR: cir.if %[[X_BOOL]] {
// CIR: %[[AGG_TMP:.*]] = cir.alloca "agg.tmp.ensured" {{.*}} : !cir.ptr<!rec_APInt>
// CIR: cir.cleanup.scope {
// CIR: %[[X2:.*]] = cir.load{{.*}} %[[X_ADDR]]
// CIR: %[[X2_BOOL:.*]] = cir.cast int_to_bool %[[X2]]
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store{{.*}} %[[FALSE]], %[[CLEANUP_COND_TRUE]]
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store{{.*}} %[[FALSE]], %[[CLEANUP_COND_FALSE]]
// CIR: cir.if %[[X2_BOOL]] {
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND_TRUE]]
// CIR: } else {
// CIR: %[[CALL_RES:.*]] = cir.call @_ZN5APInt8uadd_satEv(%[[THISVAL]])
// CIR: cir.store{{.*}} %[[CALL_RES]], %[[AGG_TMP]]
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND_FALSE]]
// CIR: }
// CIR: } cleanup normal {
// CIR: %[[F_FLAG:.*]] = cir.load{{.*}} %[[CLEANUP_COND_FALSE]]
// CIR: cir.if %[[F_FLAG]] {
// CIR: cir.call @_ZN5APIntD1Ev(%[[AGG_TMP]])
// CIR: }
// CIR: %[[T_FLAG:.*]] = cir.load{{.*}} %[[CLEANUP_COND_TRUE]]
// CIR: cir.if %[[T_FLAG]] {
// CIR: cir.call @_ZN5APIntD1Ev(%[[AGG_TMP]])
// CIR: }
// CIR: }
// CIR: }
// CIR: }
// CIR: } cleanup normal {
// CIR: cir.call @_ZN5APIntD1Ev(%[[THISVAL]])
// CIR: }
// CIR: cir.return
// LLVM: define {{.*}} void @_ZNK12APFixedPoint3addEi(ptr {{.*}} %{{.*}}, i32 {{.*}} %{{.*}})
// LLVM: %[[AGG_TMP:.*]] = alloca %struct.APInt
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
// LLVM: %[[X_ADDR:.*]] = alloca i32
// LLVM: %[[THISVAL:.*]] = alloca %struct.APInt
// LLVM: %[[CLEANUP_COND_TRUE:.*]] = alloca i8
// LLVM: %[[CLEANUP_COND_FALSE:.*]] = alloca i8
// LLVM: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_END:.*]]
// LLVM: [[OUTER_TRUE]]:
// LLVM: br i1 %{{.*}}, label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// LLVM: [[COND_TRUE]]:
// LLVM: store i8 1, ptr %[[CLEANUP_COND_TRUE]]
// LLVM: [[COND_FALSE]]:
// LLVM: %[[CALL_RES:.*]] = call %struct.APInt @_ZN5APInt8uadd_satEv(ptr {{.*}} %[[THISVAL]])
// LLVM: store %struct.APInt %[[CALL_RES]], ptr %[[AGG_TMP]]
// LLVM: store i8 1, ptr %[[CLEANUP_COND_FALSE]]
// LLVM: %[[FF:.*]] = load i8, ptr %[[CLEANUP_COND_FALSE]]
// LLVM: %[[FF_B:.*]] = trunc i8 %[[FF]] to i1
// LLVM: br i1 %[[FF_B]], label %[[CLEANUP_F:.*]], label %[[AFTER_F:.*]]
// LLVM: [[CLEANUP_F]]:
// LLVM: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[AGG_TMP]])
// LLVM: [[AFTER_F]]:
// LLVM: %[[TF:.*]] = load i8, ptr %[[CLEANUP_COND_TRUE]]
// LLVM: %[[TF_B:.*]] = trunc i8 %[[TF]] to i1
// LLVM: br i1 %[[TF_B]], label %[[CLEANUP_T:.*]], label %[[AFTER_T:.*]]
// LLVM: [[CLEANUP_T]]:
// LLVM: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[AGG_TMP]])
// LLVM: [[AFTER_T]]:
// LLVM: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[THISVAL]])
// LLVM: ret void
// OGCG: define {{.*}} void @_ZNK12APFixedPoint3addEi(ptr {{.*}} %[[THIS:.*]], i32 {{.*}} %[[X:.*]])
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
// OGCG: %[[X_ADDR:.*]] = alloca i32
// OGCG: %[[THISVAL:.*]] = alloca %struct.APInt
// OGCG: %[[AGG_TMP:.*]] = alloca %struct.APInt
// OGCG: %[[CLEANUP_COND_TRUE:.*]] = alloca i1
// OGCG: %[[CLEANUP_COND_FALSE:.*]] = alloca i1
// OGCG: br i1 %{{.*}}, label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// OGCG: [[IF_THEN]]:
// OGCG: br i1 %{{.*}}, label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// OGCG: [[COND_TRUE]]:
// OGCG: store i1 true, ptr %[[CLEANUP_COND_TRUE]]
// OGCG: [[COND_FALSE]]:
// OGCG: call void @_ZN5APInt8uadd_satEv(ptr {{.*}} sret(%struct.APInt) {{.*}} %[[AGG_TMP]], ptr {{.*}} %[[THISVAL]])
// OGCG: store i1 true, ptr %[[CLEANUP_COND_FALSE]]
// Both arms of the conditional share the same agg.tmp.ensured slot, so
// each cleanup branch destructs %[[AGG_TMP]].
// OGCG: [[COND_END:.*]]:
// OGCG: %[[FF:.*]] = load i1, ptr %[[CLEANUP_COND_FALSE]]
// OGCG: br i1 %[[FF]], label %[[CLEANUP_F:.*]], label %[[AFTER_F:.*]]
// OGCG: [[CLEANUP_F]]:
// OGCG: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[AFTER_F]]
// OGCG: [[AFTER_F]]:
// OGCG: %[[TF:.*]] = load i1, ptr %[[CLEANUP_COND_TRUE]]
// OGCG: br i1 %[[TF]], label %[[CLEANUP_T:.*]], label %[[AFTER_T:.*]]
// OGCG: [[CLEANUP_T]]:
// OGCG: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[AFTER_T]]
// OGCG: [[AFTER_T]]:
// OGCG: br label %[[IF_END]]
// OGCG: [[IF_END]]:
// OGCG: call void @_ZN5APIntD1Ev(ptr {{.*}} %[[THISVAL]])
// OGCG: ret void
struct Path { ~Path(); } g_path;
struct Iter {
~Iter();
operator bool();
};
struct Entry {
Entry();
Entry(Path);
};
// A conditional expression whose condition itself produces a temporary that
// needs cleanup (here, the Iter() temporary destroyed by ~Iter) nests the
// deferred-conditional cleanup of a temporary in one of the conditional's
// arms inside that condition's cleanup scope. The alloca for the
// conditionally-destroyed Path temporary must be hoisted out of the outer
// (full-expr) cleanup scope, even though in the freshly emitted IR its
// direct parent cleanup scope is the inner one created for the Iter
// temporary.
void makeEntry() {
Iter() ? Entry() : g_path;
}
// CIR: cir.func {{.*}} @_Z9makeEntryv()
// CIR: %[[REF_TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Iter>
// CIR: %[[CLEANUP_COND:.*]] = cir.alloca "cleanup.cond" {{.*}} : !cir.ptr<!cir.bool>
// CIR: %[[AGG_TMP0:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_Path>
// CIR: cir.cleanup.scope {
// CIR: cir.cleanup.scope {
// CIR: %[[CALL:.*]] = cir.call @_ZN4ItercvbEv(%[[REF_TMP]])
// CIR: cir.if %[[CALL]] {
// CIR: %[[ENSURED_T:.*]] = cir.alloca "agg.tmp.ensured" {{.*}} : !cir.ptr<!rec_Entry>
// CIR: cir.call @_ZN5EntryC1Ev(%[[ENSURED_T]])
// CIR: } else {
// CIR: %[[ENSURED_F:.*]] = cir.alloca "agg.tmp.ensured" {{.*}} : !cir.ptr<!rec_Entry>
// CIR: %{{.*}} = cir.get_global @g_path
// CIR: %[[TRUE:.*]] = cir.const #true
// CIR: cir.store %[[TRUE]], %[[CLEANUP_COND]]
// CIR: %[[PATH_LOAD:.*]] = cir.load{{.*}} %[[AGG_TMP0]]
// CIR: cir.call @_ZN5EntryC1E4Path(%[[ENSURED_F]], %[[PATH_LOAD]])
// CIR: }
// CIR: cir.yield
// CIR: } cleanup normal {
// CIR: cir.call @_ZN4IterD1Ev(%[[REF_TMP]])
// CIR: cir.yield
// CIR: }
// CIR: cir.yield
// CIR: } cleanup normal {
// CIR: %[[FLAG:.*]] = cir.load{{.*}} %[[CLEANUP_COND]]
// CIR: cir.if %[[FLAG]] {
// CIR: cir.call @_ZN4PathD1Ev(%[[AGG_TMP0]])
// CIR: }
// CIR: cir.yield
// CIR: }
// CIR: cir.return
// LLVM: define {{.*}} void @_Z9makeEntryv()
// LLVM: %[[ENSURED_T:.*]] = alloca %struct.Entry
// LLVM: %[[ENSURED_F:.*]] = alloca %struct.Entry
// LLVM: %[[REF_TMP:.*]] = alloca %struct.Iter
// LLVM: %[[CLEANUP_COND:.*]] = alloca i8
// LLVM: %[[AGG_TMP0:.*]] = alloca %struct.Path
// LLVM: br label %[[INIT:.*]]
// LLVM: [[INIT]]:
// LLVM: %[[CALL:.*]] = call {{.*}} i1 @_ZN4ItercvbEv(ptr {{.*}} %[[REF_TMP]])
// LLVM: br i1 %[[CALL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]]
// LLVM: [[TRUE_BB]]:
// LLVM: call void @_ZN5EntryC1Ev(ptr {{.*}} %[[ENSURED_T]])
// LLVM: br label %[[COND_END:.*]]
// LLVM: [[FALSE_BB]]:
// LLVM: store i8 1, ptr %[[CLEANUP_COND]]
// LLVM: %[[PATH_LOAD:.*]] = load %struct.Path, ptr %[[AGG_TMP0]]
// LLVM: call void @_ZN5EntryC1E4Path(ptr {{.*}} %[[ENSURED_F]], %struct.Path %[[PATH_LOAD]])
// LLVM: br label %[[COND_END]]
// LLVM: [[COND_END]]:
// LLVM: br label %[[AFTER_INNER:.*]]
// LLVM: [[AFTER_INNER]]:
// LLVM: call void @_ZN4IterD1Ev(ptr {{.*}} %[[REF_TMP]])
// LLVM: br label %[[CHECK_FLAG:.*]]
// LLVM: [[CHECK_FLAG]]:
// LLVM: %[[FLAG_BYTE:.*]] = load i8, ptr %[[CLEANUP_COND]]
// LLVM: %[[FLAG:.*]] = trunc i8 %[[FLAG_BYTE]] to i1
// LLVM: br i1 %[[FLAG]], label %[[DO_PATH_DTOR:.*]], label %[[DONE:.*]]
// LLVM: [[DO_PATH_DTOR]]:
// LLVM: call void @_ZN4PathD1Ev(ptr {{.*}} %[[AGG_TMP0]])
// LLVM: br label %[[DONE]]
// LLVM: [[DONE]]:
// LLVM: ret void
// OGCG: define {{.*}} void @_Z9makeEntryv()
// OGCG: %[[REF_TMP:.*]] = alloca %struct.Iter
// OGCG: %[[ENSURED_T:.*]] = alloca %struct.Entry
// OGCG: %[[ENSURED_F:.*]] = alloca %struct.Entry
// OGCG: %[[AGG_TMP:.*]] = alloca %struct.Path
// OGCG: %[[CLEANUP_COND:.*]] = alloca i1
// OGCG: %[[CALL:.*]] = call {{.*}} i1 @_ZN4ItercvbEv(ptr {{.*}} %[[REF_TMP]])
// OGCG: store i1 false, ptr %[[CLEANUP_COND]]
// OGCG: br i1 %[[CALL]], label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
// OGCG: [[COND_TRUE]]:
// OGCG: call void @_ZN5EntryC1Ev(ptr {{.*}} %[[ENSURED_T]])
// OGCG: br label %[[COND_END:.*]]
// OGCG: [[COND_FALSE]]:
// OGCG: store i1 true, ptr %[[CLEANUP_COND]]
// OGCG: call void @_ZN5EntryC1E4Path(ptr {{.*}} %[[ENSURED_F]], ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[COND_END]]
// OGCG: [[COND_END]]:
// OGCG: %[[IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]]
// OGCG: br i1 %[[IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]]
// OGCG: [[CLEANUP_ACTION]]:
// OGCG: call void @_ZN4PathD1Ev(ptr {{.*}} %[[AGG_TMP]])
// OGCG: br label %[[CLEANUP_DONE]]
// OGCG: [[CLEANUP_DONE]]:
// OGCG: call void @_ZN4IterD1Ev(ptr {{.*}} %[[REF_TMP]])
// OGCG: ret void