Files
llvm-project/clang/test/CIR/CodeGen/loop-cond-cleanup.cpp
Andy Kaylor a9181e8f9d [CIR] Fix CFG flattening for loops with cleanup in special regions (#187369)
If a loop required a cleanup scope in the condition or step region of
the loop, we crashed during CFG flattening because the flattening of the
cleanup scope created multiple blocks in the region, but we were
assuming there would only be one block.

This change updates the CFG flattening code to look for the
cir.condition or cir.yield operation in the last block of the region.
2026-03-18 13:44:54 -07:00

244 lines
7.6 KiB
C++

// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -fclangir -emit-cir -std=c++17 -fcxx-exceptions -fexceptions -o %t.cir
// RUN: FileCheck -check-prefixes=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -fclangir -emit-llvm -std=c++17 -fcxx-exceptions -fexceptions -o %t-cir.ll
// RUN: FileCheck -check-prefixes=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++17 -fcxx-exceptions -fexceptions -o %t.ll
// RUN: FileCheck -check-prefixes=OGCG --input-file=%t.ll %s
struct S {
S();
~S();
operator bool();
};
S makeS();
void while_cond_cleanup(int n) {
while (makeS())
--n;
}
// CIR-LABEL: cir.func {{.*}} @_Z18while_cond_cleanupi
// CIR: cir.while {
// CIR: cir.call @_Z5makeSv()
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN1ScvbEv(
// CIR: } cleanup all {
// CIR: cir.call @_ZN1SD1Ev({{.*}}) nothrow
// CIR: }
// CIR: cir.condition(
// CIR: } do {
// LLVM-LABEL: define dso_local void @_Z18while_cond_cleanupi(i32 %0) {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[TMP:.*]] = alloca %struct.S
// LLVM: call %struct.S @_Z5makeSv()
// LLVM: invoke i1 @_ZN1ScvbEv(ptr {{.*}} %[[TMP]])
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND:.*]]
// LLVM: [[CONT]]:
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: [[UNWIND]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: resume { ptr, i32 }
// LLVM: br i1
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z18while_cond_cleanupi(i32 %n) {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: while.cond:
// OGCG: %[[CALL:.*]] = invoke {{.*}} i1 @_ZN1ScvbEv(
// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
// OGCG: [[CONT]]:
// OGCG: call void @_ZN1SD1Ev(
// OGCG: br i1 %[[CALL]], label %[[BODY:.*]], label %[[END:.*]]
// OGCG: [[BODY]]:
// OGCG: br label %while.cond
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: call void @_ZN1SD1Ev(
// OGCG: [[END]]:
// OGCG: ret void
void do_while_cond_cleanup(int n) {
do {
--n;
} while (makeS());
}
// CIR-LABEL: cir.func {{.*}} @_Z21do_while_cond_cleanupi
// CIR: cir.do {
// CIR: cir.yield
// CIR: } while {
// CIR: cir.call @_Z5makeSv()
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN1ScvbEv(
// CIR: } cleanup all {
// CIR: cir.call @_ZN1SD1Ev({{.*}}) nothrow
// CIR: }
// CIR: cir.condition(
// LLVM-LABEL: define dso_local void @_Z21do_while_cond_cleanupi(i32 %0) {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[TMP:.*]] = alloca %struct.S
// LLVM: call %struct.S @_Z5makeSv()
// LLVM: invoke i1 @_ZN1ScvbEv(ptr {{.*}} %[[TMP]])
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND:.*]]
// LLVM: [[CONT]]:
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: [[UNWIND]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: resume { ptr, i32 }
// LLVM: br i1
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z21do_while_cond_cleanupi(i32 %n) {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: do.cond:
// OGCG: %[[CALL:.*]] = invoke {{.*}} i1 @_ZN1ScvbEv(
// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
// OGCG: [[CONT]]:
// OGCG: call void @_ZN1SD1Ev(
// OGCG: br i1 %[[CALL]]
// OGCG: ret void
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: call void @_ZN1SD1Ev(
void for_cond_cleanup(int n) {
for (int i = 0; makeS(); ++i)
--n;
}
// CIR-LABEL: cir.func {{.*}} @_Z16for_cond_cleanupi
// CIR: cir.for : cond {
// CIR: cir.call @_Z5makeSv()
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN1ScvbEv(
// CIR: } cleanup all {
// CIR: cir.call @_ZN1SD1Ev({{.*}}) nothrow
// CIR: }
// CIR: cir.condition(
// CIR: } body {
// LLVM-LABEL: define dso_local void @_Z16for_cond_cleanupi(i32 %0) {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[TMP:.*]] = alloca %struct.S
// LLVM: call %struct.S @_Z5makeSv()
// LLVM: invoke i1 @_ZN1ScvbEv(ptr {{.*}} %[[TMP]])
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND:.*]]
// LLVM: [[CONT]]:
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: [[UNWIND]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: resume { ptr, i32 }
// LLVM: br i1
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z16for_cond_cleanupi(i32 %n) {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: for.cond:
// OGCG: %[[CALL:.*]] = invoke {{.*}} i1 @_ZN1ScvbEv(
// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
// OGCG: [[CONT]]:
// OGCG: call void @_ZN1SD1Ev(
// OGCG: br i1 %[[CALL]], label %[[BODY:.*]], label %[[END:.*]]
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: call void @_ZN1SD1Ev(
// OGCG: [[END]]:
// OGCG: ret void
void for_step_cleanup(int n) {
for (int i = 0; i < n; (void)makeS())
--n;
}
// CIR-LABEL: cir.func {{.*}} @_Z16for_step_cleanupi
// CIR: cir.for : cond {
// CIR: } body {
// CIR: } step {
// CIR: cir.call @_Z5makeSv()
// CIR: cir.cleanup.scope {
// CIR: } cleanup all {
// CIR: cir.call @_ZN1SD1Ev({{.*}}) nothrow
// CIR: }
// CIR: }
// LLVM-LABEL: define dso_local void @_Z16for_step_cleanupi(i32 %0) {{.*}} {
// LLVM: %[[TMP:.*]] = alloca %struct.S
// LLVM: call %struct.S @_Z5makeSv()
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: br label
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z16for_step_cleanupi(i32 %n) {{.*}} {
// OGCG: for.inc:
// OGCG: call void @_Z5makeSv(
// OGCG: call void @_ZN1SD1Ev(
// OGCG: br label %for.cond
struct EndSentinel {};
struct Iter {
int operator*();
Iter &operator++();
};
S operator!=(Iter, EndSentinel);
struct Range {
Iter begin();
EndSentinel end();
};
Range getRange();
void range_for_cond_cleanup() {
for (int x : getRange()) {
(void)x;
}
}
// CIR-LABEL: cir.func {{.*}} @_Z22range_for_cond_cleanupv
// CIR: cir.for : cond {
// CIR: cir.call @_Zne4Iter11EndSentinel(
// CIR: cir.cleanup.scope {
// CIR: cir.call @_ZN1ScvbEv(
// CIR: } cleanup all {
// CIR: cir.call @_ZN1SD1Ev({{.*}}) nothrow
// CIR: }
// CIR: cir.condition(
// CIR: } body {
// LLVM-LABEL: define dso_local void @_Z22range_for_cond_cleanupv() {{.*}} personality ptr @__gxx_personality_v0 {
// LLVM: %[[TMP:.*]] = alloca %struct.S
// LLVM: call %struct.S @_Zne4Iter11EndSentinel(
// LLVM: invoke i1 @_ZN1ScvbEv(ptr {{.*}} %[[TMP]])
// LLVM: to label %[[CONT:.*]] unwind label %[[UNWIND:.*]]
// LLVM: [[CONT]]:
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: [[UNWIND]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[TMP]])
// LLVM: resume { ptr, i32 }
// LLVM: br i1
// LLVM: ret void
// OGCG-LABEL: define dso_local void @_Z22range_for_cond_cleanupv() {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: for.cond:
// OGCG: %[[CALL:.*]] = invoke {{.*}} i1 @_ZN1ScvbEv(
// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
// OGCG: [[CONT]]:
// OGCG: call void @_ZN1SD1Ev(
// OGCG: br i1 %[[CALL]], label %[[BODY:.*]], label %[[END:.*]]
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: call void @_ZN1SD1Ev(
// OGCG: [[END]]:
// OGCG: ret void